/* ============================================================
   LATENT MEDIA - main.css
   A connected studio. One signal, every channel.
   Light, warm, editorial. Signal vermilion + warm ink on bone.
   ============================================================ */

/* ---------- 1. RESET / BASE ---------- */
*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

body {
  font-family: var(--sans);
  background: var(--paper);
  color: var(--ink);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  font-synthesis: none;
  font-kerning: normal;
}

img, svg, video, canvas { display: block; max-width: 100%; }
img, video { height: auto; }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul, ol { list-style: none; }

::selection { background: var(--signal); color: var(--paper); }

:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
}

/* SKIP LINK: the offscreen "Skip to content" link must come on-screen when focused
   so keyboard users can actually use it. Overrides the inline left:-9999px. */
a[href="#main"]:focus {
  left: clamp(0.75rem, 2vw, 1.25rem) !important;
  top: 0.75rem !important;
  outline: 2px solid var(--signal);
  outline-offset: 3px;
}

/* ---------- 2. TOKENS ---------- */
:root {
  /* surfaces */
  --paper:       #F2EDE3;   /* warm bone, main bg */
  --paper-2:     #FBF8F1;   /* lighter card surface */
  --paper-3:     #EAE3D5;   /* recessed / alt band */
  --ink:         #161310;   /* warm near-black text */
  --ink-2:       #565047;   /* muted secondary text */
  --ink-3:       #8B8475;   /* faint / captions */
  --line:        #DED6C6;   /* warm hairline border */
  --line-strong: #C9BFAC;

  /* the one dark contrast surface (used sparingly) */
  --night:       #14120E;
  --night-2:     #221F18;

  /* accents */
  --signal:      #64748B;   /* vermilion - primary signal */
  --signal-deep: #475569;
  --signal-soft: #E2E8F0;
  --moss:        #2F6B57;   /* secondary category (graph / data only) */
  --moss-soft:   #DCE8E1;

  /* type - brand fonts: Space Grotesk (display + labels) + Inter (body) */
  --display: "Space Grotesk", system-ui, sans-serif;
  --sans:    "Inter", system-ui, -apple-system, sans-serif;
  --mono:    "Space Grotesk", ui-monospace, monospace;

  /* shadows: warm, layered light. Each step adds a soft ambient bed + a tighter
     contact shadow so cards read as objects sitting on warm paper, never flat, never blue.
     The brown tint (40,30,16 / 30,22,10) keeps the light feeling lamp-warm, not cold grey. */
  --sh-1: 0 1px 2px rgba(40, 30, 16, 0.05), 0 1px 1px rgba(40, 30, 16, 0.04);
  --sh-2: 0 4px 10px -4px rgba(40, 30, 16, 0.10), 0 1px 2px rgba(40, 30, 16, 0.06);
  --sh-3:
    0 22px 46px -20px rgba(40, 30, 16, 0.30),
    0 8px 18px -10px rgba(40, 30, 16, 0.14),
    0 1px 2px rgba(40, 30, 16, 0.06);
  --sh-pop:
    0 48px 96px -32px rgba(30, 22, 10, 0.46),
    0 18px 40px -24px rgba(30, 22, 10, 0.30);

  /* motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  /* layout */
  --gutter: clamp(1.25rem, 4vw, 4rem);
  --maxw: 1320px;
  --nav-h: 78px;
}

/* ---------- 3. TYPOGRAPHY ---------- */
h1, h2, h3, h4 {
  font-family: var(--display);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.02em;
  font-optical-sizing: auto;
}

.display {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 0.98;
  font-size: clamp(2.9rem, 8.5vw, 7.2rem);
}

.h1 { font-size: clamp(2.4rem, 6vw, 4.6rem); line-height: 1.0; letter-spacing: -0.022em; }
.h2 { font-size: clamp(1.9rem, 4.4vw, 3.3rem); line-height: 1.03; letter-spacing: -0.02em; }
.h3 { font-size: clamp(1.4rem, 2.6vw, 2.05rem); line-height: 1.08; letter-spacing: -0.015em; }
.h4 { font-size: clamp(1.15rem, 1.8vw, 1.4rem); line-height: 1.15; }

.serif-italic { font-style: italic; font-family: var(--display); }

em, .em { font-family: var(--display); font-style: italic; }

.lead {
  font-size: clamp(1.15rem, 1.9vw, 1.5rem);
  line-height: 1.46;
  color: var(--ink-2);
  letter-spacing: -0.011em;
  font-weight: 500;
  max-width: 44ch;
  text-wrap: pretty;
}

.mono {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 400;
}

.mono-signal { color: var(--signal); }

p { max-width: 60ch; }
.measure-tight { max-width: 44ch; }
/* let display headings break tidily rather than orphaning a word */
.h1, .h2, .display { text-wrap: balance; }

/* fluid numeral for big stats */
.numeral {
  font-family: var(--display);
  font-weight: 420;
  letter-spacing: -0.04em;
  line-height: 0.85;
  font-size: clamp(3rem, 7vw, 5.6rem);
  font-variant-numeric: tabular-nums;
}

/* ---------- 4. LAYOUT ---------- */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap-wide { max-width: 1560px; }
.wrap-narrow { max-width: 880px; }

.section { padding-block: clamp(4.5rem, 9vw, 9rem); position: relative; }
.section-tight { padding-block: clamp(3rem, 5vw, 5rem); }

.band-alt { background: var(--paper-3); }
.band-paper2 { background: var(--paper-2); }

.grid { display: grid; gap: clamp(1rem, 2.4vw, 2rem); }
.cols-12 { grid-template-columns: repeat(12, 1fr); }

.divider { height: 1px; background: var(--line); border: 0; }

.section-no {
  display: inline-flex;
  align-items: baseline;
  gap: 0.6rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.section-no b { color: var(--signal); font-weight: 400; }

/* ---------- 5. BUTTONS / LINKS ---------- */
.btn {
  --bg: var(--ink);
  --fg: var(--paper);
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.95rem 1.5rem 0.9rem;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-weight: 600;
  font-size: 0.94rem;
  letter-spacing: -0.01em;
  border-radius: 2px;
  overflow: hidden;
  transition: transform 0.5s var(--ease), background 0.3s var(--ease), color 0.3s var(--ease);
  will-change: transform;
}
.btn span { position: relative; z-index: 2; display: inline-flex; align-items: center; gap: 0.6rem; }
.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--signal);
  transform: translateY(101%);
  transition: transform 0.45s var(--ease);
  z-index: 1;
}
.btn:hover::before { transform: translateY(0); }
.btn:hover { color: #fff; }
.btn .arrow { transition: transform 0.4s var(--ease); }
.btn:hover .arrow { transform: translate(4px, -4px); }

.btn-signal { --bg: var(--signal); --fg: #fff; }
.btn-signal::before { background: var(--ink); }
.btn-signal:hover { color: var(--paper); }

.btn-ghost {
  --bg: transparent; --fg: var(--ink);
  border: 1px solid var(--line-strong);
  border-radius: 2px;
}
.btn-ghost::before { background: var(--ink); }
.btn-ghost:hover { color: var(--paper); border-color: var(--ink); }

.btn-lg { padding: 1.1rem 1.9rem; font-size: 1rem; }

/* draw-underline text link */
.link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 600;
  padding-bottom: 2px;
  color: var(--ink);
}
.link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1.5px;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease);
}
.link:hover::after { transform: scaleX(1); }
.link .arrow { transition: transform 0.35s var(--ease); }
.link:hover .arrow { transform: translateX(4px); }

/* ---------- 6. NAV ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--nav-h);
  z-index: 200;
  display: flex;
  align-items: center;
  transition: background 0.4s var(--ease), box-shadow 0.4s var(--ease), border-color 0.4s var(--ease);
  border-bottom: 1px solid transparent;
}
.nav.scrolled {
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--line);
}
.nav-inner {
  width: 100%; max-width: var(--maxw);
  margin-inline: auto; padding-inline: var(--gutter);
  display: flex; align-items: center; justify-content: space-between; gap: 2rem;
}
.brand { display: inline-flex; align-items: center; gap: 0.6rem; font-family: var(--display); font-weight: 500; font-size: 1.25rem; letter-spacing: -0.02em; }
.brand .mark { width: 26px; height: 26px; flex: none; }
.brand-sub { font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.2em; color: var(--ink-3); }
.brand-logo { height: clamp(34px, 4.6vw, 44px); width: auto; display: block; }

.nav-links { display: flex; align-items: center; gap: 0.35rem; }
.nav-links a {
  position: relative;
  padding: 0.5rem 0.9rem;
  font-weight: 550;
  font-size: 0.95rem;
  color: var(--ink-2);
  border-radius: 2px;
  transition: color 0.25s var(--ease);
}
.nav-links a::before {
  content: "";
  position: absolute; left: 0.9rem; right: 0.9rem; bottom: 0.35rem;
  height: 1.5px; background: var(--signal);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.35s var(--ease);
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::before,
.nav-links a[aria-current="page"]::before { transform: scaleX(1); }
.nav-links a[aria-current="page"] { color: var(--ink); }

.nav-cta { display: flex; align-items: center; gap: 1rem; }

.nav-toggle { display: none; width: 44px; height: 44px; position: relative; }
.nav-toggle span { position: absolute; left: 10px; right: 10px; height: 2px; background: var(--ink); transition: transform 0.35s var(--ease), opacity 0.2s; }
.nav-toggle span:nth-child(1) { top: 16px; }
.nav-toggle span:nth-child(2) { top: 22px; }
.nav-toggle span:nth-child(3) { top: 28px; }
body.menu-open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
body.menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.menu-open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* mobile overlay menu */
.mobile-menu {
  position: fixed; inset: 0; z-index: 190;
  background: var(--paper);
  display: flex; flex-direction: column;
  padding: calc(var(--nav-h) + 2rem) var(--gutter) 2rem;
  transform: translateY(-100%);
  transition: transform 0.6s var(--ease);
  pointer-events: none;
}
body.menu-open .mobile-menu { transform: translateY(0); pointer-events: auto; }
.mobile-menu a.m-link {
  font-family: var(--display);
  font-size: clamp(2rem, 9vw, 3.4rem);
  font-weight: 440;
  letter-spacing: -0.02em;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line);
  display: flex; align-items: baseline; justify-content: space-between;
  color: var(--ink);
}
.mobile-menu a.m-link .mono { font-size: 0.7rem; }
.mobile-menu .m-foot { margin-top: auto; display: flex; flex-wrap: wrap; gap: 1.5rem; justify-content: space-between; align-items: center; padding-top: 2rem; }

/* ---------- 7. HERO ---------- */
.hero { position: relative; padding-top: calc(var(--nav-h) + clamp(1.5rem, 4vw, 3.5rem)); padding-bottom: clamp(3rem, 6vw, 6rem); overflow: hidden; }
.hero-canvas { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
.hero .wrap { position: relative; z-index: 2; }

/* ---------- 8. CARDS ---------- */
.card {
  position: relative;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: clamp(1.4rem, 2.2vw, 2rem);
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease), border-color 0.4s var(--ease);
}
.card { box-shadow: var(--sh-1); }
.card:hover { box-shadow: var(--sh-3); border-color: var(--line-strong); transform: translateY(-3px); }
.card-soft { border-radius: 18px; }
.card-sharp { border-radius: 0; }

/* service node card (used in coupled picker) */
.svc-list { display: flex; flex-direction: column; }
.svc-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 1.2rem;
  padding: 1.4rem 0.5rem 1.4rem 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
  transition: padding 0.4s var(--ease), color 0.3s var(--ease);
}
.svc-row:last-child { border-bottom: 1px solid var(--line); }
.svc-row .svc-no { font-family: var(--mono); font-size: 0.72rem; color: var(--ink-3); letter-spacing: 0.1em; transition: color 0.3s; }
.svc-row .svc-name { font-family: var(--display); font-size: clamp(1.5rem, 3vw, 2.3rem); font-weight: 440; letter-spacing: -0.02em; transition: transform 0.5s var(--ease), color 0.3s; color: var(--ink); }
.svc-row .svc-arrow { opacity: 0; transform: translateX(-8px); transition: opacity 0.35s var(--ease), transform 0.35s var(--ease); color: var(--signal); }
.svc-row:hover, .svc-row.active { padding-left: 1.1rem; }
.svc-row:hover .svc-name, .svc-row.active .svc-name { color: var(--signal); }
.svc-row:hover .svc-arrow, .svc-row.active .svc-arrow { opacity: 1; transform: translateX(0); }
.svc-row:hover .svc-no, .svc-row.active .svc-no { color: var(--ink); }

/* detail panel coupled to picker */
.svc-detail { position: relative; min-height: 320px; }
.svc-pane { position: absolute; inset: 0; opacity: 0; transform: translateY(14px); transition: opacity 0.5s var(--ease), transform 0.5s var(--ease); pointer-events: none; }
.svc-pane.is-active { opacity: 1; transform: none; pointer-events: auto; position: relative; }

/* tilt / work card */
.tilt { transform-style: preserve-3d; transition: transform 0.4s var(--ease); will-change: transform; }
.tilt-inner { transform: translateZ(40px); }

.work-card {
  position: relative;
  display: block;
  border-radius: 4px;
  overflow: hidden;
  background: var(--ink);
  aspect-ratio: 4 / 3;
}
.work-card .wc-media { position: absolute; inset: 0; }
.work-card .wc-media > * { width: 100%; height: 100%; object-fit: cover; transition: transform 0.8s var(--ease); }
.work-card:hover .wc-media > * { transform: scale(1.05); }
.work-card .wc-meta {
  position: absolute; inset: auto 0 0 0; z-index: 3;
  padding: 1.3rem 1.4rem;
  color: var(--paper);
  background: linear-gradient(to top, rgba(15,12,8,0.82), rgba(15,12,8,0) 90%);
  display: flex; justify-content: space-between; align-items: flex-end; gap: 1rem;
}
.work-card .wc-tag { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.12em; color: var(--signal); }

/* stat block */
.stat { display: flex; flex-direction: column; gap: 0.4rem; }
.stat .numeral { color: var(--ink); }
.stat .stat-label { color: var(--ink-2); font-size: 0.95rem; max-width: 26ch; }

/* ---------- 9. MARQUEE-FREE LIVE TICKER (interactive, not scrolling) ---------- */
.signal-bar {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);
}
/* NOTE: the pulsing "live" dot that used to live here is banned (no-ai-slop 23).
   Nothing on this site is actually live, so a throbbing indicator was pure theatre.
   Do not reintroduce it, in any colour, in any section. */
.signal-bar .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--signal); }
.signal-now { color: var(--ink); transition: opacity 0.4s var(--ease); }

/* ---------- 10. THE CONNECTED DIAGRAM ---------- */
.diagram { position: relative; width: 100%; aspect-ratio: 1 / 1; max-width: 540px; }
.diagram canvas { position: absolute; inset: 0; }

/* ---------- 11. FOOTER ---------- */
.footer { background: var(--night); color: var(--paper); padding-block: clamp(3.5rem, 7vw, 6rem) 2.5rem; position: relative; overflow: hidden; }
.footer a { color: var(--paper); }
.footer .foot-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: clamp(1.5rem, 3vw, 3rem); }
.footer .foot-head { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 1.1rem; }
.footer .foot-link { display: block; padding: 0.35rem 0; color: color-mix(in srgb, var(--paper) 78%, transparent); transition: color 0.25s, transform 0.3s var(--ease); }
.footer .foot-link:hover { color: var(--signal); transform: translateX(4px); }
.footer .foot-big { font-family: var(--display); font-size: clamp(2.6rem, 9vw, 7rem); line-height: 0.9; letter-spacing: -0.03em; }
.footer .foot-bottom { margin-top: clamp(2.5rem, 5vw, 4rem); padding-top: 1.6rem; border-top: 1px solid rgba(255,255,255,0.12); display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: space-between; align-items: center; color: var(--ink-3); font-size: 0.85rem; }

/* ---------- 12. POPUPS / MODALS ---------- */
.modal-root { position: fixed; inset: 0; z-index: 300; display: grid; place-items: center; padding: var(--gutter); opacity: 0; pointer-events: none; transition: opacity 0.4s var(--ease); }
.modal-root.open { opacity: 1; pointer-events: auto; }
.modal-scrim { position: absolute; inset: 0; background: rgba(18, 14, 8, 0.5); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.modal {
  position: relative; z-index: 2;
  width: min(680px, 100%);
  max-height: 88vh; overflow: auto;
  background: var(--paper-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: var(--sh-pop);
  transform: translateY(20px) scale(0.98);
  opacity: 0;
  transition: transform 0.5s var(--ease), opacity 0.4s var(--ease);
}
.modal-root.open .modal { transform: none; opacity: 1; }
.modal-head { padding: clamp(1.5rem, 3vw, 2.2rem); border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
.modal-body { padding: clamp(1.5rem, 3vw, 2.2rem); }
.modal-close { width: 40px; height: 40px; border: 1px solid var(--line); border-radius: 50%; display: grid; place-items: center; flex: none; transition: background 0.3s, color 0.3s, transform 0.3s; }
.modal-close:hover { background: var(--ink); color: var(--paper); transform: rotate(90deg); }

/* mini newsletter popup (corner) */
.toast-pop {
  position: fixed; z-index: 280; right: clamp(1rem,3vw,2rem); bottom: clamp(1rem,3vw,2rem);
  width: min(380px, calc(100vw - 2rem));
  background: var(--paper-2); border: 1px solid var(--line-strong); border-radius: 6px;
  box-shadow: var(--sh-pop); padding: 1.5rem 1.5rem 1.4rem;
  transform: translateY(140%); opacity: 0;
  transition: transform 0.7s var(--ease), opacity 0.5s var(--ease);
}
.toast-pop.show { transform: none; opacity: 1; }
.toast-pop .tp-close { position: absolute; top: 0.7rem; right: 0.8rem; width: 28px; height: 28px; display: grid; place-items: center; color: var(--ink-3); border-radius: 50%; transition: color 0.2s, background 0.2s; }
.toast-pop .tp-close:hover { color: var(--ink); background: var(--paper-3); }

/* ---------- 13. FORMS ---------- */
.field { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.1rem; }
.field label { font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-2); }
.input, .textarea, .select {
  width: 100%;
  font: inherit; font-size: 1rem;
  background: var(--paper);
  border: 1px solid var(--line-strong);
  border-radius: 3px;
  padding: 0.85rem 1rem;
  color: var(--ink);
  transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease), background 0.3s;
}
.input:focus, .textarea:focus, .select:focus { outline: none; border-color: var(--signal); box-shadow: 0 0 0 3px var(--signal-soft); background: var(--paper-2); }
.textarea { resize: vertical; min-height: 120px; }

/* chip choices (real selectable - allowed functional use) */
.chips { display: flex; flex-wrap: wrap; gap: 0.55rem; }
.chip {
  padding: 0.55rem 1rem; border: 1px solid var(--line-strong); border-radius: 3px;
  font-size: 0.9rem; font-weight: 550; color: var(--ink-2);
  transition: all 0.25s var(--ease); user-select: none;
}
.chip:hover { border-color: var(--ink); color: var(--ink); }
.chip[aria-pressed="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* stepper */
.steps { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; }
.steps .step { flex: 1; height: 3px; background: var(--line); border-radius: 2px; overflow: hidden; }
.steps .step.done, .steps .step.current { background: var(--signal); }

/* ---------- 14. CUSTOM CURSOR ---------- */
.cursor-dot, .cursor-ring { position: fixed; top: 0; left: 0; z-index: 400; pointer-events: none; border-radius: 50%; mix-blend-mode: normal; }
.cursor-dot { width: 6px; height: 6px; background: var(--signal); transform: translate(-50%,-50%); transition: width 0.3s var(--ease), height 0.3s var(--ease), opacity 0.3s; }
.cursor-ring { width: 34px; height: 34px; border: 1.5px solid var(--ink); transform: translate(-50%,-50%); transition: width 0.35s var(--ease), height 0.35s var(--ease), border-color 0.3s, opacity 0.3s; }
body.cursor-active .cursor-ring { width: 56px; height: 56px; border-color: var(--signal); }
body.cursor-active .cursor-dot { opacity: 0; }
@media (hover: none), (pointer: coarse) { .cursor-dot, .cursor-ring { display: none; } }

/* ---------- 15. REVEAL ----------
   Calmer than before: shorter travel (16px), faster easing (0.62s), so reveals
   read as a quiet settle, not animation spam fighting for attention. A SAFETY
   fallback guarantees content is never permanently invisible: if the
   IntersectionObserver never fires (script error, very tall first paint, an old
   engine), the keyframe below fades every reveal in after a short delay. The JS
   sets .js-reveal on <html> on boot, which cancels the fallback for the normal
   animated path. */
[data-reveal] { opacity: 0; transform: translateY(16px); transition: opacity 0.62s var(--ease), transform 0.62s var(--ease); }
[data-reveal].in { opacity: 1; transform: none; }
[data-reveal][data-reveal="fade"] { transform: none; }
[data-reveal-stagger] > * { opacity: 0; transform: translateY(14px); transition: opacity 0.55s var(--ease), transform 0.55s var(--ease); }
[data-reveal-stagger].in > * { opacity: 1; transform: none; }

/* split-line reveal for big headings */
.reveal-lines .line { display: block; overflow: hidden; }
.reveal-lines .line > span { display: block; transform: translateY(110%); transition: transform 0.78s var(--ease); }
.reveal-lines.in .line > span { transform: none; }

/* SAFETY: no observer / no JS -> never leave content hidden. These keyframes run
   only while <html> does NOT have .js-reveal (added by JS the moment reveals init). */
@keyframes reveal-safety { to { opacity: 1; transform: none; } }
html:not(.js-reveal) [data-reveal],
html:not(.js-reveal) [data-reveal-stagger] > * {
  animation: reveal-safety 0.6s var(--ease) 1.2s forwards;
}
html:not(.js-reveal) .reveal-lines .line > span { transform: none; }
/* and if motion is reduced, show everything immediately regardless */
@media (prefers-reduced-motion: reduce) {
  [data-reveal], [data-reveal-stagger] > *, .reveal-lines .line > span { opacity: 1 !important; transform: none !important; }
}

/* ---------- 16. UTILITIES ---------- */
.signal { color: var(--signal); }
.muted { color: var(--ink-2); }
.faint { color: var(--ink-3); }
.center { text-align: center; }
.flow > * + * { margin-top: 1.1rem; }
.flow-lg > * + * { margin-top: 1.8rem; }
.row { display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
.between { justify-content: space-between; }
.pad-top { padding-top: clamp(1rem, 3vw, 2.5rem); }
.tag-mono { font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); }
.underline-art { text-decoration: underline; text-decoration-color: var(--signal); text-underline-offset: 6px; text-decoration-thickness: 2px; }
.maxw-sm { max-width: 540px; }
.maxw-md { max-width: 720px; }
.no-scroll { overflow: hidden; }

/* hover-glow ring for diagram nodes (canvas handles most) */
.kicker {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-2);
}
.kicker .ln { width: 38px; height: 1px; background: var(--line-strong); }

/* ---------- 17. RESPONSIVE ---------- */
@media (max-width: 1024px) {
  .footer .foot-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 860px) {
  .nav-links, .nav-cta .btn { display: none; }
  .nav-toggle { display: block; }
  .footer .foot-grid { grid-template-columns: 1fr 1fr; gap: 2rem; }
}
@media (max-width: 560px) {
  .footer .foot-grid { grid-template-columns: 1fr; }
  .btn { width: auto; }
}

/* ---------- 18. ACCORDION ---------- */
.accordion { border-top: 1px solid var(--line); }
.acc-item { border-bottom: 1px solid var(--line); }
.acc-item summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: baseline; justify-content: space-between; gap: 1.5rem;
  padding: 1.5rem 0;
  font-family: var(--display); font-size: clamp(1.15rem, 2.2vw, 1.6rem); font-weight: 440; letter-spacing: -0.015em;
  transition: color 0.3s var(--ease);
}
.acc-item summary::-webkit-details-marker { display: none; }
.acc-item summary:hover { color: var(--signal); }
.acc-item .acc-sign { flex: none; width: 26px; height: 26px; position: relative; margin-top: 0.4rem; }
.acc-item .acc-sign::before, .acc-item .acc-sign::after { content: ""; position: absolute; background: var(--signal); transition: transform 0.4s var(--ease); }
.acc-item .acc-sign::before { left: 0; right: 0; top: 12px; height: 2px; }
.acc-item .acc-sign::after { top: 0; bottom: 0; left: 12px; width: 2px; }
.acc-item[open] .acc-sign::after { transform: scaleY(0); }
.acc-item .acc-body { padding: 0 0 1.6rem; max-width: 60ch; color: var(--ink-2); }
.acc-item .acc-body .mono { display: block; margin-top: 0.8rem; }

/* ---------- 19. BENTO ---------- */
.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: clamp(0.8rem, 1.5vw, 1.2rem); grid-auto-rows: minmax(150px, auto); }
.bento > * { border: 1px solid var(--line); border-radius: 4px; padding: clamp(1.3rem, 2vw, 1.9rem); background: var(--paper-2); position: relative; overflow: hidden; display: flex; flex-direction: column; box-shadow: var(--sh-1); transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease), border-color 0.4s; }
.bento > *:hover { box-shadow: var(--sh-3); border-color: var(--line-strong); transform: translateY(-3px); }
.b-2 { grid-column: span 2; } .b-3 { grid-column: span 3; } .b-4 { grid-column: span 4; } .b-6 { grid-column: span 6; }
.b-r2 { grid-row: span 2; }
.b-ink { background: var(--ink); color: var(--paper); border-color: var(--ink); }
.b-ink .muted { color: color-mix(in srgb, var(--paper) 70%, transparent); }
.b-ink .mono { color: color-mix(in srgb, var(--paper) 55%, transparent); }
.b-signal { background: var(--signal); color: #fff; border-color: var(--signal); }
.b-signal .mono { color: rgba(255,255,255,0.75); }
@media (max-width: 860px) { .bento { grid-template-columns: repeat(2, 1fr); } .b-3, .b-4, .b-6 { grid-column: span 2; } .b-2 { grid-column: span 1; } }

/* ---------- 20. BIG QUOTE ---------- */
.quote-xl { font-family: var(--display); font-weight: 420; font-size: clamp(1.8rem, 4.6vw, 3.6rem); line-height: 1.08; letter-spacing: -0.02em; max-width: 22ch; }
.quote-xl .signal { font-style: italic; }

/* split layout */
.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: clamp(1.5rem, 4vw, 4.5rem); align-items: center; }
.split-40 { grid-template-columns: 0.8fr 1.2fr; }
@media (max-width: 900px) { .split, .split-40 { grid-template-columns: 1fr; } }

/* hero layout */
.hero-grid { display: grid; grid-template-columns: 1.35fr 0.65fr; gap: clamp(1.5rem, 4vw, 3.5rem); align-items: center; }
@media (max-width: 980px) { .hero-grid { grid-template-columns: 1fr; } .hero-aside { display: none; } }

/* eyebrow chapter index (intentional editorial numbering, used consistently) */
.chapter { display: inline-flex; align-items: center; gap: 0.7rem; font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 1.4rem; }
.chapter b { color: var(--signal); font-weight: 400; }
.chapter .ln { width: 30px; height: 1px; background: var(--line-strong); }

/* dark contrast section helper (use ONCE, sparingly) */
.night { background: var(--night); color: var(--paper); }
.night .lead, .night .muted { color: color-mix(in srgb, var(--paper) 72%, transparent); }
.night .card { background: var(--night-2); border-color: rgba(255,255,255,0.1); }
.night .mono { color: color-mix(in srgb, var(--paper) 55%, transparent); }
.night .divider { background: rgba(255,255,255,0.12); }

/* ============================================================
   21. HOME PAGE CRAFT PASS (iteration 1)
   Depth, pointer-parallax, coupled interactions. Light theme kept.
   ============================================================ */

/* --- parallax scene: layers shift independently of pointer --- */
[data-parallax-scene] [data-depth] { will-change: transform; }

/* warm radial light bloom behind the hero, never a blue/purple glow */
.hero-glow {
  position: absolute;
  top: -6%; right: -4%;
  width: 46vw; height: 46vw; max-width: 720px; max-height: 720px;
  z-index: 1; pointer-events: none;
  background: radial-gradient(circle at 60% 40%,
              rgba(100, 116, 139, 0.10) 0%,
              rgba(100, 116, 139, 0.04) 34%,
              transparent 66%);
  filter: blur(8px);
}
@media (max-width: 980px) { .hero-glow { width: 80vw; height: 80vw; top: -2%; right: -18%; } }

/* hero headline: a draw-on underline under the signal word */
.signal-underline { position: relative; display: inline-block; }
.signal-underline::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 0.08em;
  height: 0.07em; background: var(--signal);
  transform: scaleX(0); transform-origin: left;
  transition: transform 1.1s var(--ease) 0.7s;
}
.reveal-lines.in .signal-underline::after,
.hero-h1.in .signal-underline::after { transform: scaleX(1); }

/* hero stat strip: lead stat dominates, rest sit quieter */
.hero-stats {
  display: grid;
  grid-template-columns: auto 1px auto auto;
  align-items: end;
  gap: 0 clamp(1.4rem, 3vw, 2.4rem);
  border-top: 1px solid var(--line);
  padding-top: 1.4rem;
}
.hero-stat dt { margin: 0; }
.hero-stat dd { margin: 0.25rem 0 0; }
.hero-stat .numeral { font-size: clamp(1.7rem, 3.4vw, 2.6rem); transition: color 0.35s var(--ease); }
.hero-stat.is-lead .numeral { font-size: clamp(2.6rem, 5vw, 3.8rem); color: var(--signal); }
.hero-stats::before {
  content: ""; grid-column: 2; grid-row: 1;
  width: 1px; height: 70%; align-self: center; background: var(--line);
}
.hero-stat:not(.is-lead):hover .numeral { color: var(--signal); }
@media (max-width: 520px) {
  .hero-stats { grid-template-columns: 1fr 1fr; gap: 1.2rem 1.4rem; }
  .hero-stats::before { display: none; }
  .hero-stat.is-lead { grid-column: 1 / -1; }
}

/* framed diagram with a live readout coupled to the hovered node */
.diagram-frame {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 6px;
  background:
    radial-gradient(120% 120% at 50% 30%, var(--paper-2) 0%, transparent 70%),
    var(--paper);
  padding: clamp(0.8rem, 1.8vw, 1.4rem);
  box-shadow: var(--sh-3);
}
.diagram-frame .diagram { max-width: none; margin-inline: auto; }
.diagram-readout {
  display: flex; flex-direction: column; gap: 0.15rem;
  margin-top: 0.4rem; padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}
.diagram-readout .mono { color: var(--ink-3); }
.diagram-readout .dr-label {
  font-family: var(--display); font-style: italic;
  font-size: clamp(1.1rem, 1.8vw, 1.4rem); letter-spacing: -0.01em;
  color: var(--ink); transition: color 0.3s var(--ease);
}
.diagram-readout .dr-label.is-live { color: var(--signal); }

/* show the hero diagram on mobile (below the lede) instead of hiding it */
@media (max-width: 980px) {
  .hero-aside { display: block !important; transform: none !important; max-width: 460px; }
  .diagram-frame .diagram { aspect-ratio: 4 / 3; }
}

/* --- thesis trio: a coupled 3-phase progression, not 3 equal cards --- */
.thesis-trio {
  display: grid;
  grid-template-columns: 1fr 1.18fr 1fr;
  gap: clamp(0.9rem, 1.8vw, 1.4rem);
  align-items: stretch;
}
.thesis-card { display: flex; flex-direction: column; overflow: hidden; }
.thesis-card .tc-top { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.thesis-card .tc-step { color: var(--ink-3); }
.thesis-card.is-signal { background: var(--ink); color: var(--paper); border-color: var(--ink); transform: translateY(-12px); box-shadow: var(--sh-3); }
.thesis-card.is-signal .tc-step { color: rgba(242,237,227,0.5); }
.thesis-card.is-signal .mono { color: rgba(242,237,227,0.6); }
.thesis-card.is-signal .tc-body-dim { color: rgba(242,237,227,0.78); }
/* progressive disclosure line, hidden until hover/focus */
.thesis-card .tc-reveal {
  margin-top: auto;
  display: grid; grid-template-rows: 0fr;
  opacity: 0; transform: translateY(6px);
  transition: grid-template-rows 0.5s var(--ease), opacity 0.45s var(--ease), transform 0.45s var(--ease), padding-top 0.4s var(--ease);
  font-size: 0.95rem; color: var(--ink-2);
  overflow: hidden; padding-top: 0;
}
.thesis-card .tc-reveal > * { min-height: 0; }
.thesis-card .tc-reveal .mono { color: var(--signal); display: inline; margin-right: 0.4rem; }
.thesis-card:hover .tc-reveal,
.thesis-card:focus-within .tc-reveal {
  grid-template-rows: 1fr; opacity: 1; transform: none;
  padding-top: 1.1rem; margin-top: 1.4rem;
  border-top: 1px solid var(--line);
}
.thesis-card.is-signal:hover .tc-reveal { border-top-color: rgba(255,255,255,0.16); color: rgba(242,237,227,0.85); }
.thesis-card.is-signal:hover .tc-reveal .mono { color: var(--signal); }
.thesis-card:hover { transform: translateY(-4px); }
.thesis-card.is-signal:hover { transform: translateY(-16px); }
/* the contrast card used to carry a pulsing dot here. Removed: banned pattern,
   and the card already reads as the emphasised one from its colour and lift. */
.thesis-card .tc-pulse { display: none; }
@media (max-width: 760px) {
  .thesis-trio { grid-template-columns: 1fr; }
  .thesis-card.is-signal { transform: none; }
  .thesis-card.is-signal:hover { transform: translateY(-4px); }
}

/* --- services picker: per-system meter coupled to the selection --- */
.svc-meter { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.4rem; }
.svc-meter-num { font-family: var(--mono); font-size: 0.78rem; letter-spacing: 0.12em; color: var(--ink-3); }
.svc-meter-num [data-pane-index] { color: var(--signal); }
.svc-meter-sep { margin: 0 0.25rem; opacity: 0.6; }
.svc-meter-track { flex: 1; height: 2px; background: var(--line); position: relative; overflow: hidden; border-radius: 2px; }
.svc-meter-fill {
  position: absolute; inset: 0 auto 0 0;
  width: 100%; background: var(--signal);
  transform: scaleX(var(--pick-progress, 0.16)); transform-origin: left;
  transition: transform 0.55s var(--ease);
}
/* a soft tinted wash behind the active pane that nudges per system */
.svc-detail { isolation: isolate; }
.svc-detail::before {
  content: ""; position: absolute; z-index: -1;
  top: -1.4rem; right: -1rem; width: 42%; height: 42%;
  border-radius: 50%; opacity: 0.5; pointer-events: none;
  background: radial-gradient(circle, var(--signal-soft) 0%, transparent 70%);
  transform: translate(calc(var(--pick-progress, 0.16) * 30px), calc(var(--pick-progress, 0.16) * -14px));
  transition: transform 0.6s var(--ease);
}
.svc-pane.is-active { animation: svc-pane-in 0.55s var(--ease); }
@keyframes svc-pane-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
.svc-pane h3 .em, .svc-pane h3 { transition: none; }

/* --- numbers: one hero stat dominates, the rest stack quietly --- */
.stat-row { display: grid; grid-template-columns: 1fr 1.1fr; gap: clamp(1.5rem, 4vw, 4rem); align-items: stretch; }
.stat-hero {
  justify-content: flex-end;
  border-top: 2px solid var(--ink);
  padding-top: 1.4rem;
}
.stat-hero .numeral { font-size: clamp(5rem, 13vw, 11rem); color: var(--signal); line-height: 0.82; }
.stat-hero .stat-label { font-size: 1.15rem; max-width: 24ch; color: var(--ink); margin-top: 0.6rem; }
.stat-stack { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.4rem, 3vw, 2.4rem); align-content: start; }
.stat-stack .stat { padding-top: 1.1rem; border-top: 1px solid var(--line); transition: border-color 0.4s var(--ease); }
.stat-stack .stat:hover { border-top-color: var(--signal); }
.stat-stack .numeral { font-size: clamp(2.6rem, 5vw, 4rem); }
@media (max-width: 760px) {
  .stat-row { grid-template-columns: 1fr; }
  .stat-stack { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 460px) { .stat-stack { grid-template-columns: 1fr; } }

/* --- bento flywheel: make the compounding card read as a loop --- */
.b-ink .flywheel { margin-top: auto; padding-top: 1.2rem; }
.flywheel-loop { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.08em; }
.flywheel-loop .fw-node { color: var(--paper); opacity: 0.55; transition: opacity 0.3s var(--ease), color 0.3s var(--ease); }
.flywheel-loop .fw-arrow { color: var(--signal); opacity: 0.7; }
.b-ink:hover .flywheel-loop .fw-node { opacity: 0.9; }

/* speed stat: emphasise the multiplier */
.b-signal .numeral { line-height: 0.85; }

/* --- magnetic press feedback on primary buttons --- */
.btn:active { transform: scale(0.97); }
.btn-lg { box-shadow: var(--sh-2); }
.btn-signal.btn-lg:hover { box-shadow: var(--sh-3); }

/* ============================================================
   22. GLOBAL COMPONENT CRAFT PASS (iteration 5)
   Polish the shared UI that appears on all six pages: nav, buttons,
   links, modals, stepper, toast, form fields, chips, cursor.
   Additive only. No hook renamed or removed. Light theme kept.
   ============================================================ */

/* --- NAV: scroll progress hairline + condensed scrolled state --- */
.nav::after {
  content: "";
  position: absolute; left: 0; bottom: -1px;
  height: 2px; width: 100%;
  transform: scaleX(var(--scroll-progress, 0)); transform-origin: left;
  background: linear-gradient(90deg, var(--signal), var(--signal-deep));
  opacity: 0; transition: opacity 0.4s var(--ease);
  pointer-events: none;
}
.nav.scrolled::after { opacity: 1; }
.nav.scrolled { box-shadow: 0 1px 0 rgba(40,30,16,0.04), 0 10px 30px -24px rgba(40,30,16,0.4); }
/* the active page gets a small travelling marker under the label, plus the underline */
.nav-links a[aria-current="page"]::after {
  content: ""; position: absolute; left: 50%; bottom: 0.05rem;
  width: 4px; height: 4px; border-radius: 50%; background: var(--signal);
  transform: translate(-50%, 6px) scale(0); opacity: 0;
  transition: transform 0.45s var(--ease) 0.1s, opacity 0.4s var(--ease);
}
.nav.scrolled .nav-links a[aria-current="page"]::after { transform: translate(-50%, 0) scale(1); opacity: 1; }
/* brand mark answers the cursor with a tiny lift */
.brand { transition: transform 0.4s var(--ease); }
.brand:hover { transform: translateY(-1px); }
.brand .mark svg circle:last-of-type { transition: transform 0.4s var(--ease); }

/* the nav Start-a-project button: a quick light sweep on hover (transform only) */
.nav-cta .btn::after {
  content: ""; position: absolute; top: 0; bottom: 0; left: -40%;
  width: 32%; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.22), transparent);
  transform: translateX(0) skewX(-14deg); opacity: 0;
}
.nav-cta .btn:hover::after { animation: nav-sheen 0.7s var(--ease); }
@keyframes nav-sheen { 0% { transform: translateX(0) skewX(-14deg); opacity: 0; } 30% { opacity: 1; } 100% { transform: translateX(420%) skewX(-14deg); opacity: 0; } }

/* --- BUTTONS: resting depth + a touch of overshoot on the arrow --- */
.btn { box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset, 0 1px 2px rgba(40,30,16,0.05); }
.btn:hover { box-shadow: var(--sh-2); }
.btn-signal:hover .arrow, .btn:hover .arrow { transform: translate(4px, -4px); }
.btn .arrow { will-change: transform; }
.btn-ghost:hover { box-shadow: var(--sh-2); }

/* --- LINKS: the underline draws AND a small lead dot slides in --- */
.link::before {
  content: ""; position: absolute; left: -2px; bottom: 1px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--signal);
  transform: scale(0); transform-origin: center;
  transition: transform 0.35s var(--ease);
}
.link:hover::before { transform: scale(1); }
.link:hover { color: var(--signal); }
.link.no-dot::before { display: none; }

/* --- MODALS: a softer spring on open, panel cross-fade, animated steps --- */
.modal { transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.4s var(--ease); }
.modal-root.open .modal { transform: none; }
.modal-scrim { transition: opacity 0.4s var(--ease), backdrop-filter 0.4s var(--ease); }
/* head gets a quiet warm wash so the modal reads as a designed object */
.modal-head { background: linear-gradient(180deg, var(--paper-2), color-mix(in srgb, var(--paper-3) 38%, var(--paper-2))); position: relative; }
.modal-head::after { content: ""; position: absolute; left: clamp(1.5rem,3vw,2.2rem); bottom: -1px; width: 40px; height: 2px; background: var(--signal); }
/* step panels animate in when shown (the stepper swaps [hidden]) */
.step-panel { animation: step-in 0.45s var(--ease); }
@keyframes step-in { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
/* the steps progress bar: each segment fills smoothly, current one pulses faintly */
.steps .step { position: relative; background: var(--line); }
.steps .step::after {
  content: ""; position: absolute; inset: 0; border-radius: 2px;
  background: var(--signal); transform: scaleX(0); transform-origin: left;
  transition: transform 0.5s var(--ease);
}
.steps .step.done::after, .steps .step.current::after { transform: scaleX(1); }
.steps .step.current::after { background: linear-gradient(90deg, var(--signal), var(--signal-deep)); }
/* keep the old solid-fill rule from clashing: let the ::after carry the colour */
.steps .step.done, .steps .step.current { background: var(--line); }

/* --- TOAST: smoother lift. The pulsing dot that sat before the label is gone;
       it was the exact [blinking light] + [tiny caps label] unit the brief bans. --- */
.toast-pop { transition: transform 0.7s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.5s var(--ease); }
.toast-pop .mono-signal { position: relative; padding-left: 0; }
.toast-pop .mono-signal::before { content: none; }

/* --- FORM FIELDS: focused field lifts + its label switches to signal --- */
.field { position: relative; transition: transform 0.3s var(--ease); }
.field:focus-within { transform: translateY(-1px); }
.field:focus-within label { color: var(--signal); }
.input, .textarea, .select { will-change: border-color, box-shadow; }
.input:focus, .textarea:focus, .select:focus { box-shadow: 0 0 0 3px var(--signal-soft), 0 6px 18px -12px rgba(40,30,16,0.4); }

/* --- CHIPS: tactile press + a check that scales in when selected ---
   chips hold bare text (no inner span) on all 6 pages, so we colour the
   element directly and keep any decoration BEHIND with z-index, never over text. */
.chip {
  position: relative;
  transition: background 0.28s var(--ease), color 0.22s var(--ease), border-color 0.25s var(--ease), transform 0.16s var(--ease), box-shadow 0.25s var(--ease), padding-right 0.28s var(--ease);
}
.chip:hover { transform: translateY(-1px); box-shadow: var(--sh-1); }
.chip:active { transform: scale(0.94); }
/* the check tick belongs to MULTI-SELECT chips (enquiry + project modal), not the
   single-select work filter (which carries data-filter). Scope it away from filters. */
.chip:not([data-filter]) { transition: background 0.28s var(--ease), color 0.22s var(--ease), border-color 0.25s var(--ease), transform 0.16s var(--ease), box-shadow 0.25s var(--ease), padding-right 0.28s var(--ease); }
.chip:not([data-filter])[aria-pressed="true"] { box-shadow: var(--sh-2); padding-right: 1.9rem; }
.chip:not([data-filter])::after {
  content: ""; position: absolute; right: 0.7rem; top: 50%;
  width: 9px; height: 5px; margin-top: -4px;
  border-left: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
  transform: rotate(-45deg) scale(0); transform-origin: center;
  opacity: 0; transition: transform 0.3s var(--ease), opacity 0.25s var(--ease);
}
.chip:not([data-filter])[aria-pressed="true"]::after { transform: rotate(-45deg) scale(1); opacity: 1; }

/* --- FORM SUCCESS: animate in instead of a hard swap (global) --- */
.btn.is-sending { pointer-events: none; }
.btn.is-sending span { opacity: 0.55; }
.btn.is-sending .arrow { animation: send-arrow 0.6s var(--ease) infinite; }
@keyframes send-arrow { 0% { transform: translate(0,0); } 50% { transform: translate(5px,-5px); opacity: 0.3; } 51% { transform: translate(-5px,5px); opacity: 0; } 52% { opacity: 0.3; } 100% { transform: translate(0,0); opacity: 1; } }
.form-done.is-done { animation: done-rise 0.5s var(--ease) both; }
@keyframes done-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.form-done.is-done .numeral { animation: done-pop 0.6s cubic-bezier(0.16, 1, 0.3, 1) 0.08s both; transform-origin: center; }
@keyframes done-pop { 0% { transform: scale(0.3); opacity: 0; } 60% { transform: scale(1.12); } 100% { transform: scale(1); opacity: 1; } }
.form-done.is-done h3, .form-done.is-done .mono-signal { animation: done-rise 0.5s var(--ease) 0.18s both; }
.form-done.is-done p { animation: done-rise 0.5s var(--ease) 0.26s both; }

/* --- CURSOR: a press state + a soft text-hover state --- */
.cursor-ring { transition: width 0.35s var(--ease), height 0.35s var(--ease), border-color 0.3s, opacity 0.3s, transform 0.06s linear; }
body.cursor-press .cursor-ring { width: 26px; height: 26px; border-color: var(--signal); }
body.cursor-press .cursor-dot { opacity: 1; width: 5px; height: 5px; }
body.cursor-text .cursor-ring { width: 6px; height: 30px; border-radius: 2px; border-color: var(--signal); }
body.cursor-text .cursor-dot { opacity: 0; }

/* ============================================================
   23. SHARED VISUAL SYSTEMS CRAFT PASS (iteration 6)
   Refine the systems that appear on every page so the whole site lifts at once:
   the framed diagram, the chapter/section devices, depth on the framed visuals,
   and a tighter, warmer editorial type rhythm. Additive only, light theme kept,
   no documented hook renamed or removed.
   ============================================================ */

/* --- THE FRAMED DIAGRAM: read it as a crafted instrument, not a chart in a box --- */
.diagram-frame {
  /* warm layered light: a soft top sheen over the paper, with real depth beneath */
  background:
    radial-gradient(130% 90% at 50% 0%, var(--paper-2) 0%, transparent 62%),
    linear-gradient(180deg, color-mix(in srgb, var(--paper-2) 92%, #fff), var(--paper));
  box-shadow: var(--sh-3);
  overflow: hidden;
}
/* a single faint corner index so the frame reads as a labelled plate, not a naked box.
   one quiet mono mark, top-left, never a pill or a colour strip. */
.diagram-frame::before {
  content: "FIG.";
  position: absolute; top: 0.9rem; left: 1.1rem; z-index: 2;
  font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.22em;
  color: var(--ink-3); opacity: 0.7; pointer-events: none;
}
/* the live readout gains a small leading signal node so it reads as a live instrument */
.diagram-readout .dr-label { position: relative; }
.diagram-readout .dr-label.is-live { color: var(--signal); }

/* --- CHAPTER + SECTION devices: a finer editorial number, hairline that animates in --- */
.chapter b, .section-no b { font-variant-numeric: tabular-nums; }
.chapter .ln, .section-no .ln, .kicker .ln {
  position: relative; overflow: hidden;
}
/* a thin signal trace travels the chapter hairline once it reveals, so the number device
   feels alive without being a load-spam fade. transform-only, reduced-motion safe. */
.chapter .ln::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--signal), transparent);
  transform: translateX(-100%);
}
[data-reveal].in .chapter .ln::after,
.in .chapter .ln::after { animation: chapter-trace 2.2s var(--ease) 0.4s; }
@keyframes chapter-trace { 0% { transform: translateX(-100%); } 60%, 100% { transform: translateX(220%); } }

/* --- NUMERAL: a touch more optical polish on the big editorial figures --- */
.numeral { font-feature-settings: "ss01"; }

/* --- DEPTH CONSISTENCY: the framed/ink contrast cards sit on the same warm light --- */
.svc-detail { will-change: auto; }

/* reduced-motion: kill the chapter trace and any decorative diagram motion cue */
@media (prefers-reduced-motion: reduce) {
  .chapter .ln::after, .section-no .ln::after { animation: none !important; display: none; }
}

/* ============================================================
   24. FINAL COHESION + ACCESSIBILITY PASS (iteration 7)
   Whole-site focus visibility, consistent custom-control focus rings, and a
   few small cohesion touches. Additive only, light theme kept, no hook renamed.
   ============================================================ */

/* --- FOCUS: every custom interactive control shows a clear keyboard ring ---
   The global :focus-visible outline can be clipped by overflow:hidden surfaces
   (work tiles, chips, summaries), so give those an inset ring that always shows. */
.svc-row:focus-visible,
.acc-item summary:focus-visible,
.eng-switch button:focus-visible,
.vs-switch button:focus-visible,
.flywheel-steps .fw:focus-visible,
.filterbar .chip:focus-visible,
.car-dots b:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
  border-radius: 2px;
}
/* work tiles + framed cards clip their content, so use an inset box-shadow ring
   that survives overflow:hidden instead of an outline that gets cut off */
.work-card:focus-visible,
.tile-face:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--paper), 0 0 0 4px var(--signal), var(--sh-3);
}
/* modal close button: a visible focus ring (it sits on a warm wash) */
.modal-close:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* --- CONTRAST: nudge the faintest caption ink up slightly so muted/caption text
   on warm paper clears a more comfortable contrast bar without changing the look.
   --ink-3 was #8B8475 (~3.6:1 on paper); #847D6E reads a touch stronger. --- */
:root { --ink-3: #847D6E; }

/* --- COHESION: a consistent resting + hover depth on the page-level alt cards
   that were defined inline per page, so card elevation reads the same site-wide.
   (Scoped narrowly; each page's own larger lifts still win by source order.) --- */
.next-step, .approach-step, .glyph, .coord-card, .coord-plate { box-shadow: var(--sh-1); }

/* ============================================================
   23. KINETIC LAYER
   The site used to sit still and wait to be read. This layer makes it
   move under the reader: a lerped scroll, paper grain, type that arrives
   word by word, a scroll-velocity tape, and one long pinned sequence
   where a single brief travels through all six systems.
   Additive. Every effect degrades back to the old static page when JS is
   off, the pointer is coarse, or reduced motion is on.
   ============================================================ */

/* --- 23.1 PAPER GRAIN -------------------------------------------------
   Bone paper with no tooth reads like a screen. A fixed noise plate at
   very low opacity gives the whole site a printed surface, and because it
   blends with overlay it also works over the dark band and the footer. */
.lm-grain {
  position: fixed; inset: 0;
  z-index: 240;
  pointer-events: none;
  opacity: 0.055;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
  background-size: 190px 190px;
}
@media (prefers-reduced-motion: reduce) { .lm-grain { opacity: 0.035; } }

/* --- 23.2 SMOOTH SCROLL ----------------------------------------------
   When the lerped scroller is driving, native smooth-scroll has to get out
   of the way or the two fight each other on every anchor jump. */
html.lm-smooth { scroll-behavior: auto; }

/* --- 23.3 KINETIC TYPE ------------------------------------------------
   Words rise out of their own clipped line box, each a beat behind the
   last. Same idea as .reveal-lines but per word, so a long headline reads
   as a sentence being spoken rather than a block being switched on. */
.kin { display: block; }
.kin .kin-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.08em;   /* room for descenders inside the clip box */
  margin-bottom: -0.08em;
}
.kin .kin-w > i {
  display: inline-block;
  font-style: inherit;
  transform: translateY(105%) rotate(2.5deg);
  opacity: 0;
  transition:
    transform 0.9s cubic-bezier(0.16, 1, 0.3, 1),
    opacity 0.5s var(--ease);
  transition-delay: var(--kd, 0ms);
}
.kin.in .kin-w > i { transform: none; opacity: 1; }
html:not(.js-reveal) .kin .kin-w > i { transform: none; opacity: 1; }

/* --- 23.4 SCRUB ------------------------------------------------------
   Elements drift at their own rate as their section crosses the viewport.
   Transform only, never layout. */
[data-scrub] { will-change: transform; }

/* --- 23.5 THE TAPE ---------------------------------------------------
   A running band of the six system names. Not decoration: it moves at the
   speed you scroll, and each word fills solid as it crosses the playhead,
   so the band behaves like tape running past a head. */
.tape {
  position: relative;
  overflow: hidden;
  padding-block: clamp(1.7rem, 3.4vw, 3rem);
  border-block: 1px solid var(--line);
  background: var(--paper-2);
  --tape-head: 50%;
}
.tape-track {
  display: flex;
  align-items: center;
  width: max-content;
  will-change: transform;
}
.tape-w {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(2.2rem, 6.4vw, 5rem);
  line-height: 1.06;
  letter-spacing: -0.03em;
  padding-inline: clamp(0.8rem, 2vw, 1.7rem);
  color: transparent;
  -webkit-text-stroke: 1px var(--line-strong);
  transition: color 0.35s var(--ease), -webkit-text-stroke-color 0.35s var(--ease), opacity 0.35s var(--ease);
  white-space: nowrap;
  opacity: 0.8;
}
.tape-w.lit {
  color: var(--ink);
  -webkit-text-stroke-color: transparent;
  opacity: 1;
}
.tape-sep {
  flex: none;
  width: 9px; height: 9px;
  background: var(--signal);
  opacity: 0.36;
  transform: rotate(45deg);
  border-radius: 2px;
}
/* the playhead: a hairline the words pass through */
.tape::before {
  content: "";
  position: absolute;
  left: var(--tape-head); top: 0; bottom: 0;
  width: 1px;
  background: linear-gradient(180deg, transparent, var(--signal) 20%, var(--signal) 80%, transparent);
  opacity: 0.45;
  pointer-events: none;
  z-index: 2;
}
.tape-read {
  position: absolute;
  left: var(--tape-head);
  bottom: 0.5rem;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--signal);
  z-index: 3;
  white-space: nowrap;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .tape::before, .tape-read { display: none; }
  .tape-w { color: var(--ink); -webkit-text-stroke-color: transparent; }
}

/* --- 23.6 THE CHAIN: pinned horizontal sequence -----------------------
   One brief walking through six systems. On desktop the section pins and
   the track slides sideways as you scroll, so the reader physically travels
   along the chain. Under 1024px it collapses to a normal vertical stack:
   the same content, none of the pinning risk on touch. */
.chain {
  position: relative;
  background: var(--paper-3);
  border-block: 1px solid var(--line);
}
.chain-sticky { position: relative; overflow: hidden; }
/* the section title holds still while the stages travel past it, which is
   the whole argument of the section stated as a piece of behaviour */
.chain-head {
  padding: clamp(2.5rem, 6vw, 4rem) var(--gutter) clamp(1.2rem, 2.4vw, 2rem);
  max-width: var(--maxw);
  margin-inline: auto;
  width: 100%;
}
.chain-head .chain-title {
  font-family: var(--display);
  font-weight: 500;
  letter-spacing: -0.028em;
  line-height: 1.02;
  font-size: clamp(1.9rem, 4.4vw, 3.2rem);
  max-width: 20ch;
}
.chain-track {
  display: flex;
  flex-direction: column;
  gap: clamp(0.6rem, 2vw, 1.2rem);
  padding: 0 var(--gutter) clamp(3rem, 6vw, 5rem);
}
.chain-stage {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* plain inline flow, not flex: the mark has to sit inside the sentence and
   keep its punctuation tight against it, the way a paste-up would */
.chain-line {
  display: block;
  font-family: var(--display);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.2;
  font-size: clamp(1.9rem, 4.4vw, 3.4rem);
  max-width: 22ch;
  color: var(--ink);
  text-wrap: balance;
}
.chain-desc {
  display: block;
  width: 100%;
  font-family: var(--sans);
  font-weight: 500;
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  letter-spacing: -0.01em;
  line-height: 1.45;
  color: var(--ink-2);
  max-width: 34ch;
  margin-top: 1.1rem;
}

/* the marker word. Not a chip: an irregular hand-cut block of colour with
   the word knocked out of it, set on a slight angle like a paste-up. */
.chain-mark {
  display: inline-block;
  position: relative;
  padding: 0.14em 0.34em 0.2em;
  margin-inline: 0.04em -0.04em;
  line-height: 1;
  vertical-align: -0.14em;
  color: var(--paper);
  background: var(--ink);
  clip-path: polygon(1.4% 7%, 99.2% 0%, 98.4% 93%, 0.4% 100%);
  transform: rotate(var(--tilt, -1.6deg));
  font-weight: 600;
  letter-spacing: -0.035em;
}
.chain-mark.m-signal { background: var(--signal); color: #fff; }
.chain-mark.m-deep   { background: var(--signal-deep); color: #fff; }
.chain-mark.m-moss   { background: var(--moss); color: var(--paper); }

/* the chain graphic: a rail with a node per stage. Segments fill as the
   sequence advances, so the picture states the argument the words make. */
.chain-rail {
  position: relative;
  display: flex;
  align-items: center;
  margin-top: clamp(1.4rem, 3vw, 2.2rem);
}
.chain-node {
  position: relative;
  flex: none;
  width: 11px; height: 11px;
  border-radius: 50%;
  background: var(--paper-3);
  border: 1.5px solid var(--line-strong);
  transition: background 0.5s var(--ease), border-color 0.5s var(--ease), transform 0.5s var(--ease);
}
.chain-stage.on .chain-node { background: var(--signal); border-color: var(--signal); transform: scale(1.25); }
.chain-seg {
  flex: 1;
  height: 1.5px;
  background: var(--line-strong);
  position: relative;
  overflow: hidden;
  min-width: 40px;
}
.chain-seg::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.75s var(--ease);
}
.chain-stage.on .chain-seg::after { transform: scaleX(var(--f, 1)); }
.chain-no {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  color: var(--ink-3);
  margin-left: 0.9rem;
  white-space: nowrap;
  transition: color 0.4s var(--ease);
}
.chain-stage.on .chain-no { color: var(--signal); }

.chain-close .chain-line { max-width: 17ch; }

@media (min-width: 1024px) {
  .chain { --stage-w: 60vw; }
  html.js-reveal .chain-sticky {
    position: sticky; top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  html.js-reveal .chain-head {
    flex: none;
    padding-top: calc(var(--nav-h) + clamp(1rem, 2.5vw, 2.2rem));
    padding-bottom: clamp(0.8rem, 1.8vw, 1.6rem);
  }
  html.js-reveal .chain-viewport { flex: 1; min-height: 0; overflow: hidden; display: flex; align-items: center; }
  html.js-reveal .chain-track {
    flex-direction: row;
    gap: 0;
    padding: 0;
    height: 100%;
    align-items: center;
    will-change: transform;
  }
  html.js-reveal .chain-stage {
    flex: none;
    width: var(--stage-w);
    height: 100%;
    justify-content: center;
    /* left edge lines up with .wrap so stage 01 sits under the title */
    padding-inline: var(--gutter) clamp(2.5rem, 6vw, 6rem);
  }
  html.js-reveal .chain-pad { flex: none; width: calc(max((100vw - var(--maxw)) / 2, 0px)); height: 100%; }
  /* the closer has to be the last thing on screen when the pin releases, so the
     tail pad pushes the stage before it fully out of frame */
  html.js-reveal .chain-pad-end { width: calc(100vw - var(--stage-w)); }
  .chain-line { font-size: clamp(2.2rem, 3.8vw, 3.8rem); max-width: 17ch; }
  .chain-rail { margin-top: clamp(2rem, 3.4vw, 3rem); max-width: 30rem; }
  /* stages animate in as the track carries them into place */
  html.js-reveal .chain-stage .chain-line,
  html.js-reveal .chain-stage .chain-desc,
  html.js-reveal .chain-stage .chain-rail {
    opacity: 0;
    transform: translateY(26px);
    transition: opacity 0.6s var(--ease), transform 0.75s cubic-bezier(0.16, 1, 0.3, 1);
  }
  html.js-reveal .chain-stage .chain-mark {
    transform: rotate(var(--tilt, -1.6deg)) scale(0.68);
    opacity: 0;
    transition: transform 0.62s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.4s var(--ease);
  }
  /* these MUST carry the same html.js-reveal scope as the hidden state above.
     Without it the hidden rule wins on specificity and no stage ever appears. */
  html.js-reveal .chain-stage.on .chain-line { opacity: 1; transform: none; transition-delay: 0.02s; }
  html.js-reveal .chain-stage.on .chain-mark { opacity: 1; transform: rotate(var(--tilt, -1.6deg)) scale(1); transition-delay: 0.12s; }
  html.js-reveal .chain-stage.on .chain-desc { opacity: 1; transform: none; transition-delay: 0.16s; }
  html.js-reveal .chain-stage.on .chain-rail { opacity: 1; transform: none; transition-delay: 0.22s; }
}

/* stacked (mobile/tablet) chain: the rail turns vertical so the hand-off
   between systems still reads as one continuous line. */
@media (max-width: 1023.98px) {
  .chain-pad { display: none; }
  /* no gap: the rail down the left has to read as one unbroken line */
  .chain-track { gap: 0; }
  .chain-stage {
    padding: clamp(1.5rem, 5vw, 2.2rem) 0 clamp(1.5rem, 5vw, 2.2rem) clamp(1.5rem, 6vw, 2.2rem);
    border-left: 1.5px solid var(--line-strong);
  }
  .chain-stage::before {
    content: "";
    position: absolute; left: -1.5px; top: 0; bottom: 0; width: 1.5px;
    background: var(--signal);
    transform: scaleY(0); transform-origin: top;
    transition: transform 0.9s var(--ease);
  }
  .chain-stage.on::before { transform: scaleY(1); }
  .chain-stage::after {
    content: "";
    position: absolute; left: -6px; top: clamp(1.5rem, 5vw, 2.2rem);
    width: 11px; height: 11px; border-radius: 50%;
    background: var(--paper-3); border: 1.5px solid var(--line-strong);
    transition: background 0.5s var(--ease), border-color 0.5s var(--ease);
  }
  .chain-stage.on::after { background: var(--signal); border-color: var(--signal); }
  .chain-rail { display: none; }
  .chain-stage .chain-line, .chain-stage .chain-desc {
    opacity: 0; transform: translateY(22px);
    transition: opacity 0.6s var(--ease), transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
  }
  .chain-stage.on .chain-line { opacity: 1; transform: none; }
  .chain-stage.on .chain-desc { opacity: 1; transform: none; transition-delay: 0.1s; }
}

@media (prefers-reduced-motion: reduce) {
  .chain-sticky { position: relative !important; height: auto !important; display: block !important; }
  .chain-viewport { overflow: visible !important; display: block !important; }
  .chain-track {
    transform: none !important;
    flex-direction: column !important;
    height: auto !important;
    gap: clamp(2rem, 5vw, 3rem) !important;
    padding: 0 var(--gutter) clamp(3rem, 6vw, 5rem) !important;
  }
  .chain-stage { width: auto !important; height: auto !important; padding-inline: 0 !important; }
  .chain-pad { display: none !important; }
  .chain-hint { display: none !important; }
  .chain-stage .chain-line,
  .chain-stage .chain-desc,
  .chain-stage .chain-rail { opacity: 1 !important; transform: none !important; }
  .chain-stage .chain-mark { opacity: 1 !important; transform: rotate(var(--tilt, -1.6deg)) !important; }
}

/* 3) no JS at all: nothing is ever left invisible waiting for a class that will
      never arrive. The stages simply stack and read as a list. */
html:not(.js-reveal) .chain-stage .chain-line,
html:not(.js-reveal) .chain-stage .chain-desc,
html:not(.js-reveal) .chain-stage .chain-rail { opacity: 1; transform: none; }
html:not(.js-reveal) .chain-stage .chain-mark { opacity: 1; transform: rotate(var(--tilt, -1.6deg)); }
html:not(.js-reveal) .chain-stage::before { transform: scaleY(1); }
html:not(.js-reveal) .chain-hint { display: none; }

/* --- 23.7 CHAIN PROGRESS READOUT -------------------------------------- */
.chain-hint {
  position: absolute;
  right: clamp(1.5rem, 4vw, 3rem);
  bottom: clamp(1.5rem, 4vw, 2.4rem);
  display: none;
  align-items: center;
  gap: 0.7rem;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  z-index: 3;
}
@media (min-width: 1024px) { .chain-hint { display: flex; } }
.chain-hint .ch-track { width: 56px; height: 1px; background: var(--line-strong); position: relative; overflow: hidden; }
.chain-hint .ch-track::after {
  content: ""; position: absolute; inset: 0;
  background: var(--signal);
  transform: scaleX(var(--chain-progress, 0)); transform-origin: left;
}

/* --- 23.8 PICKER MARKER ----------------------------------------------
   The service list used to change colour and nothing else. A marker now
   travels between rows, so the selection has continuity the way a real
   physical index would. */
.svc-list { position: relative; }
.svc-marker {
  position: absolute;
  left: 0; top: 0;
  width: 2px;
  background: var(--signal);
  transform: translateY(var(--my, 0px));
  height: var(--mh, 0px);
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1), height 0.5s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s;
  pointer-events: none;
  opacity: 0;
}
.svc-list.marker-ready .svc-marker { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .svc-marker { transition: opacity 0.3s; } }

/* --- 23.9 CHAPTER RAIL ------------------------------------------------
   A live index of the page down the left edge. It is a real control (each
   entry jumps to its section) and it reports where you are, which is the
   only reason it earns the space. */
.chapter-rail {
  position: fixed;
  left: clamp(0.9rem, 1.8vw, 1.6rem);
  top: 50%;
  transform: translateY(-50%);
  z-index: 190;
  display: none;
  flex-direction: column;
  gap: 0.55rem;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
.chapter-rail.show { opacity: 1; }
@media (min-width: 1360px) { .chapter-rail { display: flex; } }
.chapter-rail a {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: color 0.3s var(--ease);
}
.chapter-rail a .cr-tick {
  width: 16px; height: 1.5px;
  background: var(--line-strong);
  transition: width 0.45s var(--ease), background 0.35s var(--ease);
  flex: none;
}
.chapter-rail a .cr-name {
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
  white-space: nowrap;
}
/* names stay folded away until the rail is asked for, so the index never
   competes with the page content it is indexing */
.chapter-rail:hover a .cr-name { opacity: 0.7; transform: none; }
.chapter-rail:hover a.current .cr-name { opacity: 1; }
.chapter-rail a:hover { color: var(--ink); }
.chapter-rail a:hover .cr-tick { width: 26px; background: var(--ink); }
.chapter-rail a.current { color: var(--signal); }
.chapter-rail a.current .cr-tick { width: 30px; background: var(--signal); }
/* the pinned chain owns the full width while it runs, so the rail steps back */
.chapter-rail.hushed { opacity: 0 !important; pointer-events: none; }

/* --- 23.10 WORK TILE MOTION -------------------------------------------
   The tiles were already good objects; they just did not respond to the
   page moving. The art inside now drifts against its frame on scroll, and
   the tile arrives with a wipe instead of a plain fade. */
.work-card .wc-fig, .work-card .wc-bg { will-change: transform; }
.wipe-in {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1s cubic-bezier(0.16, 1, 0.3, 1);
}
.wipe-in.in { clip-path: inset(0 0 0 0); }
html:not(.js-reveal) .wipe-in { clip-path: none; }
@media (prefers-reduced-motion: reduce) { .wipe-in { clip-path: none !important; } }

/* --- 23.11 CURSOR LABEL -----------------------------------------------
   The ring already grew on interactive targets. Where the action is not
   obvious from the element itself, it now says what it does. */
.cursor-label {
  position: fixed; top: 0; left: 0;
  z-index: 401;
  pointer-events: none;
  font-family: var(--mono);
  font-size: 0.58rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink);
  padding: 0.3rem 0.55rem;
  border-radius: 2px;
  opacity: 0;
  transform: translate(-50%, calc(-50% + 34px)) scale(0.9);
  transition: opacity 0.25s var(--ease), transform 0.35s var(--ease);
  white-space: nowrap;
}
body.cursor-labelled .cursor-label { opacity: 1; transform: translate(-50%, calc(-50% + 34px)) scale(1); }

/* --- 23.12 SECTION EDGE WIPE ------------------------------------------
   A single hairline draws itself across the top of a band as the band
   arrives, so sections announce themselves without an eyebrow label. */
.edge-draw { position: relative; }
.edge-draw::before {
  content: "";
  position: absolute; left: 0; right: 0; top: 0;
  height: 1px;
  background: var(--signal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}
.edge-draw.in::before { transform: scaleX(1); }
