/* =================================================================
   WaveIT — Design tokens
   ================================================================= */
:root {
  /* Chapter surfaces */
  --ink: #0A0E17;
  --ink-2: #10151F;
  --ink-3: #182031;
  --ink-deep: #05070C;
  --paper: #F4F1EA;
  --paper-2: #FFFFFF;

  /* Pale blue / cool neutral — a second light surface, distinct from paper */
  --pale: #E7EDFA;
  --pale-2: #F3F6FD;
  --pale-text: #1B2542;
  --pale-text-soft: #4C577A;
  --pale-line: rgba(27, 37, 66, 0.14);
  --pale-line-strong: rgba(27, 37, 66, 0.24);

  /* No third accent colour — WaveIT blue carries every "payoff"
     moment too, at a deeper/brighter tone rather than a new hue */
  --signal-strong: #1F45B8;

  /* Text on ink */
  --text: #F3F5FB;
  --text-soft: rgba(243, 245, 251, 0.68);
  --text-mute: rgba(243, 245, 251, 0.44);

  /* Text on paper */
  --ink-text: #14213D;
  --ink-text-soft: #4E5871;
  --ink-text-mute: #5A6278;

  /* Brand accent */
  --signal: #3D7BFF;
  --signal-deep: #2557D6;
  --signal-hover: #2D5FE0;
  --signal-bright: #7FA6FF;
  --signal-soft: rgba(61, 123, 255, 0.12);

  /* Climax / blue chapter */
  --blue-1: #2454DD;
  --blue-2: #16307A;
  --blue-deep: #0B1B4A;

  /* Lines */
  --line: rgba(243, 245, 251, 0.10);
  --line-strong: rgba(243, 245, 251, 0.20);
  --line-on-paper: rgba(20, 33, 61, 0.14);
  --line-on-paper-strong: rgba(20, 33, 61, 0.26);

  /* Semantic */
  --error: #FF7A6B;

  /* Type — one sans-serif family throughout. Character comes from
     weight contrast (900 against 400/500 in the same family), not
     from a second typeface. Space Mono is reserved for small
     numeric/functional marks only, never for UI chrome. */
  --font-sans: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-display: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: "Space Mono", "SFMono-Regular", Consolas, monospace;

  /* Shape */
  --radius: 4px;
  --radius-lg: 10px;

  /* Motion */
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in: cubic-bezier(0.55, 0, 1, 0.45);
  --dur-fast: 200ms;
  --dur-med: 550ms;
  --dur-slow: 1000ms;

  /* Layout */
  --wrap: 1220px;
  --header-h: 72px;
}

/* =================================================================
   Reset & base
   ================================================================= */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
ul, ol { margin: 0; padding: 0; list-style: none; }
p { margin: 0; }
h1, h2, h3, h4, dt, dd { margin: 0; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea { font: inherit; color: inherit; }
svg { display: block; }

.icon-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 1rem; top: -5rem;
  background: var(--signal-deep); color: #fff;
  padding: 0.75rem 1.25rem; border-radius: var(--radius);
  z-index: 1000; transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: 1rem; }

:focus-visible { outline: 2px solid var(--signal-bright); outline-offset: 3px; }
.chapter--paper :focus-visible, .chapter--pale :focus-visible { outline-color: var(--signal-deep); }
/* Chapter headings take programmatic focus after a nav transition
   purely so screen readers announce the new section — sighted users
   already got that cue from the sweep itself. Chrome's focus-visible
   heuristic still draws a ring around script-focused non-interactive
   elements regardless of input device, which reads as a stray box
   around the heading on every click. Focus still moves (the AT
   announcement still fires); only the visible ring is suppressed. */
.section__heading:focus-visible,
.contact__heading:focus-visible {
  outline: none;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 24px; }
@media (min-width: 640px) { .wrap { padding: 0 40px; } }

/* =================================================================
   Chapters — the page's colour fields
   ================================================================= */
.chapter { position: relative; }
.chapter--ink { background: var(--ink); color: var(--text); }
.chapter--ink-deep { background: var(--ink-deep); color: var(--text); }
.chapter--paper { background: var(--paper); color: var(--ink-text); }
.chapter--pale { background: var(--pale); color: var(--pale-text); }
.chapter--blue { background: linear-gradient(155deg, var(--blue-1) 0%, var(--blue-2) 100%); color: #fff; }
.chapter--climax { background: linear-gradient(155deg, var(--blue-2) 0%, var(--blue-deep) 100%); color: #fff; }

/* =================================================================
   The signal — no longer a line dragged down every chapter edge.
   It appears with intent in five places: the hero graphic, the
   system diagram's spokes, the performance timeline, the modal's
   entrance, and the nav-transition mark. Everywhere else, continuity
   is implied through colour and rhythm, not drawn.

   Chapter seams are a confident diagonal cut between the two colours
   either side — a section edge, not a squiggle — with a soft signal-
   coloured glow riding the cut line itself.
   ================================================================= */
.seam { position: relative; height: 64px; overflow: hidden; }
@media (min-width: 1024px) { .seam { height: 104px; } }
.seam::before {
  content: "";
  position: absolute; inset: -2px;
  filter: drop-shadow(0 0 10px var(--seam-glow, rgba(61,123,255,0.35)));
}
.seam--ink-to-paper { background: var(--paper); }
.seam--ink-to-paper::before { background: var(--ink); clip-path: polygon(0 0, 100% 0, 100% 100%, 0 42%); }

.seam--paper-to-blue { background: var(--blue-1); --seam-glow: rgba(244,241,234,0.4); }
.seam--paper-to-blue::before { background: var(--paper); clip-path: polygon(0 0, 100% 0, 100% 58%, 0 100%); }

.seam--blue-to-pale { background: var(--pale); }
.seam--blue-to-pale::before { background: var(--blue-2); clip-path: polygon(0 0, 100% 0, 100% 100%, 0 42%); }

.seam--pale-to-ink { background: var(--ink); --seam-glow: rgba(37,87,214,0.3); }
.seam--pale-to-ink::before { background: var(--pale); clip-path: polygon(0 0, 100% 0, 100% 58%, 0 100%); }

.seam--ink-to-climax { background: var(--blue-2); }
.seam--ink-to-climax::before { background: var(--ink); clip-path: polygon(0 0, 100% 0, 100% 100%, 0 42%); }

@media (max-width: 640px) {
  .seam { height: 44px; }
}

/* =================================================================
   Ambient motion layer — one shared component (built once by JS,
   see initAmbient in script.js), recoloured per chapter via
   --ambient-a / --ambient-b. Two sine waves at different speeds and
   opacities drift slowly left; a chapter's rail crossing their span
   is what occasionally reads as the signal "connecting" with them.
   Same slow, linear, uninterrupted motion as the rest of the page.
   Respects reduced motion (frozen, faint, still present).
   ================================================================= */
.ambient {
  position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0;
  --ambient-a: rgba(61, 123, 255, 0.16);
  --ambient-b: rgba(61, 123, 255, 0.09);
  transition: transform 900ms var(--ease);
  /* Recede through the middle of the chapter, where the reading
     content lives, and stay visible only in the quieter top/bottom
     margins — so a wave never reads as cutting through a heading
     or a diagram, only drifting in the space around them. */
  mask-image: linear-gradient(to bottom, black 0%, black 10%, transparent 24%, transparent 76%, black 90%, black 100%);
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 10%, transparent 24%, transparent 76%, black 90%, black 100%);
}
.chapter--paper .ambient, .chapter--pale .ambient { --ambient-a: rgba(37, 87, 214, 0.12); --ambient-b: rgba(37, 87, 214, 0.07); }
.chapter--blue .ambient, .chapter--climax .ambient { --ambient-a: rgba(255,255,255,0.15); --ambient-b: rgba(255,255,255,0.08); }
.ambient svg { position: absolute; width: 220%; height: 100%; left: 0; top: 0; }
.ambient__wave { fill: none; stroke-width: 1.4; vector-effect: non-scaling-stroke; }
.ambient__wave--a { stroke: var(--ambient-a); animation: ambient-drift 64s linear infinite; }
.ambient__wave--b { stroke: var(--ambient-b); stroke-width: 1; animation: ambient-drift 96s linear infinite reverse; }
@keyframes ambient-drift { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (max-width: 767px) {
  .ambient__wave--b { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .ambient__wave--a, .ambient__wave--b { animation: none !important; }
  .ambient { transform: none !important; }
}

/* =================================================================
   Spotlight — a soft glow that follows the pointer through dark
   chapters (see initSpotlight in script.js). Desktop hover only;
   purely decorative, so it never intercepts clicks. This is the
   "the page should feel alive" layer: it costs nothing structural,
   it's just proof the surface is reacting to you.
   ================================================================= */
.spotlight {
  position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden;
  opacity: 0; transition: opacity 500ms var(--ease);
  background: radial-gradient(560px circle at var(--sx, 50%) var(--sy, 30%), rgba(127, 166, 255, 0.16), transparent 62%);
}
.spotlight.is-active { opacity: 1; }
@media (hover: none) { .spotlight { display: none; } }
@media (prefers-reduced-motion: reduce) { .spotlight { display: none; } }

/* =================================================================
   Custom cursor — a small dot that tracks the pointer exactly and a
   ring that eases a little behind it (see initCustomCursor in
   script.js, which does the rAF-eased tracking and adds is-hover on
   anything clickable). Only enabled — and only hides the native
   cursor — on fine-pointer/hover devices with motion allowed; a
   touch device or prefers-reduced-motion never sees any of this.
   ================================================================= */
html.has-custom-cursor, html.has-custom-cursor * { cursor: none !important; }
.cursor-dot, .cursor-ring {
  position: fixed; top: 0; left: 0; pointer-events: none; z-index: 900;
  border-radius: 50%; opacity: 0; will-change: transform;
}
html.has-custom-cursor .cursor-dot, html.has-custom-cursor .cursor-ring { opacity: 1; }
.cursor-dot {
  width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  background: var(--signal-deep);
  transition: opacity 200ms var(--ease), transform 150ms var(--ease);
}
.cursor-ring {
  width: 34px; height: 34px; margin: -17px 0 0 -17px;
  border: 1.5px solid rgba(37, 87, 214, 0.55);
  transition: opacity 200ms var(--ease), width 260ms var(--ease), height 260ms var(--ease), margin 260ms var(--ease), border-color 260ms var(--ease), background 260ms var(--ease);
}
.cursor-dot.is-hover { transform: scale(0); }
.cursor-ring.is-hover {
  width: 64px; height: 64px; margin: -32px 0 0 -32px;
  border-color: var(--signal-deep); background: rgba(37, 87, 214, 0.08);
}
/* .cta pills already show their own hover state (arrow chip grows,
   magnetic nudge) — a 64px ring on top of that read as the button
   itself warping, so the cursor steps aside instead of doubling up. */
.cursor-dot.is-suppressed, .cursor-ring.is-suppressed { opacity: 0 !important; }
/* Dark chapters read better with the brighter tone — toggled by JS
   (initCustomCursor) via elementFromPoint, since these are fixed
   overlay elements and never actual descendants of a chapter. */
.cursor-ring.is-on-dark { border-color: rgba(127, 166, 255, 0.65); }
.cursor-ring.is-on-dark.is-hover { border-color: var(--signal-bright); background: rgba(127, 166, 255, 0.12); }
.cursor-dot.is-on-dark { background: var(--signal-bright); }
@media (hover: none), (pointer: coarse) { .cursor-dot, .cursor-ring { display: none !important; } }
@media (prefers-reduced-motion: reduce) { .cursor-dot, .cursor-ring { display: none !important; } }

/* =================================================================
   Scroll rail — a fixed line + travelling dot that marks how far
   down the page you are. Docked to the edge, out of the reading
   column, so it never crosses content the way the old always-on
   rail did (see DESIGN.md v6). Desktop only: on a short mobile
   viewport a page-length indicator like this reads as clutter, not
   wayfinding, so it's hidden below 900px.
   ================================================================= */
.scroll-rail {
  display: none;
  position: fixed; left: 30px; top: 50%; z-index: 40;
  height: min(46vh, 400px); width: 2px;
  transform: translateY(-50%);
  pointer-events: none;
}
@media (min-width: 900px) { .scroll-rail { display: block; } }
.scroll-rail__track {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(61,123,255,0) 0%, rgba(61,123,255,0.28) 12%, rgba(61,123,255,0.28) 88%, rgba(61,123,255,0) 100%);
}
.scroll-rail__dot {
  position: absolute; left: 50%; top: 0; width: 7px; height: 7px; border-radius: 50%;
  background: var(--signal-bright);
  box-shadow: 0 0 0 4px rgba(127,166,255,0.18), 0 0 12px rgba(127,166,255,0.55);
  transform: translate(-50%, 0);
  will-change: transform;
}
@media (prefers-reduced-motion: reduce) {
  .scroll-rail__dot { transition: none !important; }
}

/* =================================================================
   Chapter-transition sweep — reuses the loader's curtain language
   for nav travel between chapters (see wireNavTransitions in JS).
   ================================================================= */
.sweep { position: fixed; inset: 0; z-index: 500; pointer-events: none; }
.sweep.is-active { pointer-events: auto; } /* blocks input to the page underneath for the full covered duration */
.sweep__panel {
  position: absolute; inset: 0; background: var(--ink);
  transform: translateX(-100%); will-change: transform;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 22px;
}
/* Cover and leave are the ONLY transform transitions — both wait for
   a real transitionend in JS before the next step runs, so the jump
   underneath can never happen before the panel is fully opaque.
   Timing target: ~250ms cover, ~250ms mark resolve, then a hold
   before the hidden jump, ~250ms leave — about 900ms total. */
.sweep.is-covering .sweep__panel,
.sweep.is-leaving .sweep__panel { transition: transform 250ms var(--ease); }
.sweep.is-covering .sweep__panel { transform: translateX(0); }
.sweep.is-leaving .sweep__panel { transform: translateX(100%); }

.sweep__mark { display: flex; overflow: hidden; opacity: 0; }
.sweep__word { font-size: clamp(1.75rem, 1.2rem + 2vw, 2.75rem); font-weight: 800; letter-spacing: -0.02em; display: inline-block; transform: translateY(60%); }
.sweep__word--wave { color: var(--text); }
.sweep__word--it { color: var(--signal); }
.sweep.is-covered .sweep__mark { opacity: 1; }
.sweep.is-covered .sweep__word { transition: transform 220ms var(--ease); }
.sweep.is-covered .sweep__word--it { transition-delay: 60ms; }
.sweep.is-covered .sweep__word { transform: translateY(0); }
.sweep.is-leaving .sweep__mark { opacity: 0; transition: opacity 120ms var(--ease); }

.sweep__signal { width: 200px; height: 8px; overflow: visible; }
.sweep__signal-line { stroke: var(--signal); stroke-width: 2; stroke-linecap: round; stroke-dasharray: 240; stroke-dashoffset: 240; }
.sweep.is-covered .sweep__signal-line { transition: stroke-dashoffset 280ms var(--ease) 80ms; stroke-dashoffset: 0; }
.sweep.is-leaving .sweep__signal-line { transition: none; stroke-dashoffset: 240; }

@media (prefers-reduced-motion: reduce) {
  .sweep, .sweep__panel, .sweep__mark, .sweep__word, .sweep__signal-line { transition: none !important; }
}

/* =================================================================
   Type primitives
   ================================================================= */
.eyebrow {
  font-family: var(--font-sans); font-size: 0.9375rem; font-weight: 600;
  letter-spacing: 0.01em; color: var(--signal-bright);
  margin-bottom: 0.9rem;
}
.section__heading {
  font-family: var(--font-display);
  font-size: clamp(1.85rem, 1.3rem + 2.4vw, 3.25rem);
  font-weight: 800; line-height: 1.05; letter-spacing: -0.02em; max-width: 20ch;
}
.section__heading--on-blue { color: #fff; }
.section__lead { font-size: 1.0625rem; line-height: 1.6; color: var(--text-soft); max-width: 46ch; margin-top: 1rem; }
.chapter--paper .section__lead { color: var(--ink-text-soft); }
.section__lead--on-blue { color: rgba(255,255,255,0.88); }

.section { padding: 88px 0; position: relative; z-index: 1; }
@media (min-width: 1024px) { .section { padding: 132px 0; } }
.section__header { margin-bottom: 48px; position: relative; z-index: 1; }
@media (min-width: 1024px) { .section__header { margin-bottom: 64px; } }

/* =================================================================
   Buttons
   ================================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.55rem;
  font-family: var(--font-sans); font-size: 0.9375rem; font-weight: 600;
  letter-spacing: 0;
  padding: 15px 26px; border-radius: var(--radius); border: 1px solid transparent;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  white-space: nowrap;
}
.btn:active { transform: translateY(1px) scale(0.98); }
.btn--primary { background: var(--signal-deep); color: #fff; }
.btn--primary:hover { background: var(--signal-hover); }
.btn--ghost { background: transparent; color: var(--text); border-color: var(--line-strong); }
.btn--ghost:hover { border-color: var(--text); background: rgba(243,245,251,0.05); }
.contact .btn--ghost, .contact .btn--primary { border-color: rgba(255,255,255,0.4); }
.btn--compact { padding: 11px 18px; font-size: 0.875rem; }
.btn--full { width: 100%; padding: 16px 24px; }
.btn--large { padding: 18px 32px; font-size: 1rem; }

/* =================================================================
   CTA — the one conversion action on the page, built to be
   impossible to miss: a pill with its own contrasting arrow chip,
   not a generic rounded rectangle. The arrow nudges and the chip
   grows slightly on hover/focus; press settles it back down.
   ================================================================= */
.cta {
  position: relative; display: inline-flex; align-items: center;
  background: var(--signal-deep); border-radius: 999px;
  padding: 8px 8px 8px 26px; gap: 18px;
  transition: background 260ms var(--ease), box-shadow 260ms var(--ease), transform 150ms var(--ease);
  box-shadow: 0 1px 0 rgba(255,255,255,0.06) inset, 0 14px 30px rgba(21,58,158,0.28);
}
.cta:hover { background: var(--signal-hover); box-shadow: 0 1px 0 rgba(255,255,255,0.08) inset, 0 18px 38px rgba(21,58,158,0.38); }
.cta:active { transform: scale(0.98); }
.cta__label { font-size: 1.0625rem; font-weight: 700; color: #fff; white-space: nowrap; }
.cta__arrow {
  flex-shrink: 0; width: 48px; height: 48px; border-radius: 50%;
  background: #fff; color: var(--signal-deep);
  display: flex; align-items: center; justify-content: center;
  transition: transform 300ms var(--ease), width 300ms var(--ease);
}
.cta__arrow svg { width: 20px; height: 20px; stroke: currentColor; stroke-width: 2; fill: none; stroke-linecap: round; stroke-linejoin: round; transition: transform 300ms var(--ease); }
.cta:hover .cta__arrow { width: 56px; }
.cta:hover .cta__arrow svg { transform: translateX(3px); }

.cta--compact { padding: 6px 6px 6px 20px; gap: 12px; }
.cta--compact .cta__label { font-size: 0.9375rem; }
.cta--compact .cta__arrow { width: 36px; height: 36px; }
.cta--compact .cta__arrow svg { width: 16px; height: 16px; }
.cta--compact:hover .cta__arrow { width: 42px; }

.cta--large { padding: 10px 10px 10px 32px; gap: 22px; }
.cta--large .cta__label { font-size: 1.1875rem; }
.cta--large .cta__arrow { width: 58px; height: 58px; }
.cta--large .cta__arrow svg { width: 22px; height: 22px; }
.cta--large:hover .cta__arrow { width: 66px; }

.cta--full { width: 100%; justify-content: space-between; }

.cta--on-blue { background: #fff; box-shadow: 0 1px 0 rgba(20,33,61,0.05) inset, 0 14px 30px rgba(5,10,30,0.3); }
.cta--on-blue:hover { background: #fff; box-shadow: 0 1px 0 rgba(20,33,61,0.05) inset, 0 18px 38px rgba(5,10,30,0.4); }
.cta--on-blue .cta__label { color: var(--signal-deep); }
.cta--on-blue .cta__arrow { background: var(--signal-deep); color: #fff; }

/* Mobile CTA sizing — a floating pill at desktop scale reads as
   oversized once the viewport shrinks to a phone. Each size step
   gets its own reduction in padding, label size and arrow-chip
   diameter rather than a single blanket shrink, so header/hero/
   contact stay context-appropriate instead of visually identical. */
@media (max-width: 640px) {
  .cta--large { padding: 5px 5px 5px 20px; gap: 12px; }
  .cta--large .cta__label { font-size: 0.9375rem; }
  .cta--large .cta__arrow { width: 40px; height: 40px; }
  .cta--large .cta__arrow svg { width: 17px; height: 17px; }
  .cta--large:hover .cta__arrow { width: 44px; }
}
@media (max-width: 420px) {
  .site-header__row { gap: 10px; }
  .wordmark { font-size: 1.1875rem; }
  .menu-toggle { width: 34px; height: 34px; }
}

/* =================================================================
   Loader
   ================================================================= */
.loader { position: fixed; inset: 0; z-index: 1000; background: var(--ink-deep); display: flex; align-items: center; justify-content: center; }
.loader[hidden] { display: none; }
.loader__inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; gap: 22px; transition: opacity 420ms var(--ease), transform 420ms var(--ease); }
.loader.is-leaving .loader__inner { opacity: 0; transform: scale(0.96); }
.loader__mark { font-size: clamp(2.25rem, 8vw, 3.25rem); font-weight: 800; letter-spacing: -0.02em; display: flex; overflow: hidden; }
.loader__word { display: inline-block; transform: translateY(115%); transition: transform 700ms var(--ease); }
.loader__word--wave { color: var(--text); }
.loader__word--it { color: var(--signal); }
.loader.is-active .loader__word { transform: translateY(0); }
.loader.is-active .loader__word--it { transition-delay: 90ms; }
.loader__signal { width: 280px; height: 8px; overflow: visible; }
.loader__signal-line { stroke: var(--signal); stroke-width: 2; stroke-linecap: round; stroke-dasharray: 400; stroke-dashoffset: 400; }
.loader.is-active .loader__signal-line { transition: stroke-dashoffset 700ms var(--ease) 260ms; stroke-dashoffset: 0; }
.loader__status { font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-mute); opacity: 0; transition: opacity 400ms var(--ease) 500ms; }
.loader.is-active .loader__status { opacity: 1; }
.loader__curtain { position: absolute; top: 0; bottom: 0; width: 51%; background: var(--ink-deep); z-index: 1; transition: transform 650ms var(--ease-in); }
.loader__curtain--l { left: 0; transform-origin: left; }
.loader__curtain--r { right: 0; transform-origin: right; }
.loader.is-leaving .loader__curtain--l { transform: translateX(-100%); }
.loader.is-leaving .loader__curtain--r { transform: translateX(100%); }
@media (prefers-reduced-motion: reduce) { .loader { display: none !important; } }

/* =================================================================
   Reveal systems
   ================================================================= */
[data-reveal-mask] { display: inline-block; transform: translateY(112%); opacity: 0; }
[data-reveal-mask].is-revealed { animation: mask-up var(--dur-slow) var(--ease) forwards; }
@keyframes mask-up { to { transform: translateY(0); opacity: 1; } }
.hero__mask { display: block; overflow: hidden; }

[data-reveal-fade] { opacity: 0; transform: translateY(18px); transition: opacity 700ms var(--ease), transform 700ms var(--ease); }
[data-reveal-fade].is-revealed { opacity: 1; transform: translateY(0); }

[data-reveal-lines] .word { display: inline-block; overflow: hidden; }
[data-reveal-lines] .word span { display: inline-block; transform: translateY(105%); }
[data-reveal-lines].is-revealed .word span { animation: mask-up 900ms var(--ease) forwards; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  [data-reveal-mask], [data-reveal-fade], [data-reveal-lines] .word span {
    opacity: 1 !important; transform: none !important; animation: none !important; transition: none !important;
  }
  .loader__word { transform: none !important; }
  .primary-nav__indicator { transition: none !important; }
  .sequence__dot, .migrate__dot { animation: none !important; }
  .tree__node, .converge__leaf, .converge__trunk, .converge__hub, .sequence__dot, .migrate__dot {
    transition: none !important; opacity: 1 !important; transform: none !important;
  }
  .migrate__dot { display: none !important; }

  .sweep, .sweep__panel, .sweep__signal, .sweep__signal-line { transition: none !important; }
}

/* =================================================================
   Header / nav
   ================================================================= */
.site-header {
  position: sticky; top: 0; z-index: 100;
  /* Near-opaque with a heavy blur — not the ~0.86 + 10px it was.
     That combination let bold heading text scrolling underneath
     show through as a legible, moving ghost (worst over the blue
     chapter's high-contrast white-on-blue headings); this fully
     obscures it into a soft, static colour instead. */
  background: rgba(8, 11, 18, 0.97);
  backdrop-filter: blur(28px) saturate(140%); -webkit-backdrop-filter: blur(28px) saturate(140%);
  border-bottom: 1px solid var(--line);
}
.site-header__row { height: var(--header-h); display: flex; align-items: center; justify-content: space-between; gap: 24px; }
.wordmark { font-size: 1.375rem; font-weight: 800; letter-spacing: -0.01em; display: inline-flex; }
.wordmark__wave { color: var(--text); }
.wordmark__it { color: var(--signal); }

.primary-nav { position: relative; display: none; }
.primary-nav__list { display: flex; align-items: center; gap: 30px; }
.primary-nav__list a {
  font-family: var(--font-sans); font-size: 0.9375rem; font-weight: 500; letter-spacing: 0;
  color: var(--text-soft); display: inline-block; padding: 4px 0; transition: color var(--dur-fast) var(--ease);
}
.primary-nav__list a:hover { color: var(--text); }
.primary-nav__indicator {
  position: absolute; left: 0; bottom: -1px; width: 1px; height: 2px; background: var(--signal);
  transform: translateX(0) scaleX(0); transform-origin: left center;
  transition: transform 380ms var(--ease), opacity 200ms var(--ease); opacity: 0;
}
.primary-nav__indicator.is-visible { opacity: 1; }
@media (min-width: 900px) { .primary-nav { display: block; } }

.site-header__actions { display: flex; align-items: center; gap: 16px; }
/* Below the breakpoint where the primary nav collapses into the
   hamburger, the compact pill next to it just crowds the header —
   the mobile menu already carries the full-size CTA, so it isn't
   lost, only deferred to where there's room for it. */
.site-header__actions .cta--compact { display: none; }
@media (min-width: 900px) { .site-header__actions .cta--compact { display: inline-flex; } }

.menu-toggle { display: flex; align-items: center; width: 40px; height: 40px; justify-content: center; }
@media (min-width: 900px) { .menu-toggle { display: none; } }
.menu-toggle__box { width: 22px; height: 14px; position: relative; }
.menu-toggle__bar { position: absolute; left: 0; right: 0; height: 2px; background: var(--text); transition: transform var(--dur-fast) var(--ease), top var(--dur-fast) var(--ease); }
.menu-toggle__bar:first-child { top: 0; }
.menu-toggle__bar:last-child { top: 12px; }
.menu-toggle[aria-expanded="true"] .menu-toggle__bar:first-child { top: 6px; transform: rotate(45deg); }
.menu-toggle[aria-expanded="true"] .menu-toggle__bar:last-child { top: 6px; transform: rotate(-45deg); }

.mobile-menu { position: fixed; inset: var(--header-h) 0 0 0; background: var(--ink); padding: 32px 24px 40px; overflow-y: auto; z-index: 90; }
@media (min-width: 900px) { .mobile-menu { display: none !important; } }
.mobile-menu__list { display: flex; flex-direction: column; gap: 4px; margin-bottom: 28px; }
.mobile-menu__list a { display: block; font-size: 1.75rem; font-weight: 700; letter-spacing: -0.01em; padding: 12px 0; border-bottom: 1px solid var(--line); }
.mobile-menu .btn { width: 100%; }
.mobile-menu__meta { margin-top: 20px; font-family: var(--font-mono); font-size: 0.8125rem; letter-spacing: 0.04em; color: var(--text-mute); }

/* =================================================================
   Hero
   ================================================================= */
.hero { padding: 64px 0 0; overflow: hidden; position: relative; }
@media (min-width: 1024px) { .hero { padding: 76px 0 0; } }

.hero__inner {
  position: relative; z-index: 1; width: 100%;
  padding-top: 72px; padding-bottom: 88px;
}
@media (min-width: 768px) { .hero__inner { padding-top: 96px; padding-bottom: 108px; } }
@media (min-width: 1024px) { .hero__inner { padding-top: 128px; padding-bottom: 132px; } }

.hero__meta {
  font-family: var(--font-mono); font-size: 0.8125rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--signal-bright);
  margin-bottom: 22px;
}
.hero__meta span { color: var(--text-mute); margin: 0 10px; }

.hero__headline { margin: 0; max-width: 1180px; }
.hero__display {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(3rem, 1.6rem + 7.2vw, 7.75rem);
  line-height: 0.98; letter-spacing: -0.03em; color: var(--text);
}
.hero__display--soft { font-weight: 400; color: var(--text-soft); }

/* The lead sentence and the actions share one row on wide screens —
   copy on the left, the CTA anchored to its own baseline on the
   right — so the width below the headline is claimed by one
   composed unit instead of an isolated block floating in empty
   space. Everything shares the headline's own left edge; nothing is
   right-aligned in isolation. Mobile stacks full-width instead. */
.hero__foot {
  margin-top: 44px;
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 28px 48px;
}
.hero__lead { flex: 1 1 380px; max-width: 46ch; min-width: 0; }
.hero__actions { flex: 0 1 auto; min-width: 0; margin-top: 0; }
@media (min-width: 1024px) { .hero__foot { margin-top: 60px; } }

/* Scroll-linked depth: the headline drifts slightly as the hero
   scrolls past, no JS involved. Pure progressive enhancement —
   browsers without scroll-driven animations just see the static
   headline, which is already the complete, correct experience. */
@supports (animation-timeline: view()) {
  @media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
    [data-parallax] {
      animation: hero-parallax linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 60%;
    }
    @keyframes hero-parallax { from { transform: translateY(0); } to { transform: translateY(-32px); } }
  }
}
.hero__lead { font-size: 1.1875rem; line-height: 1.55; color: var(--text-soft); opacity: 0; transform: translateY(18px); transition: opacity 700ms var(--ease), transform 700ms var(--ease); }
.hero__lead.is-revealed { opacity: 1; transform: translateY(0); }
.hero__actions { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 32px; opacity: 0; transform: translateY(18px); transition: opacity 700ms var(--ease), transform 700ms var(--ease); }
.hero__actions.is-revealed { opacity: 1; transform: translateY(0); }
@media (min-width: 640px) { .hero__actions { margin-top: 0; } }

/* The hero signal — structural, not ambient. A deliberate line-art
   composition standing in for "the systems WaveIT keeps running,"
   woven full-bleed behind the headline rather than boxed into a
   graphic column — the composition is one authored arrangement, not
   text-plus-decoration. Present (at reduced opacity, no point labels)
   on mobile too: its own composition, not the desktop graphic hidden. */
.hero__signal { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; opacity: 0.68; }
.hero__signal-path { fill: none; stroke: var(--signal); stroke-width: 2; opacity: 0.5; stroke-linecap: round; stroke-dasharray: 1900; stroke-dashoffset: 1900; transition: stroke-dashoffset 1800ms var(--ease); }
.hero__signal.is-revealed .hero__signal-path { stroke-dashoffset: 0; }
/* Points, not labels — the line reads as woven texture in the
   negative space, not a diagram explaining itself with captions
   competing against the hero copy above it for reading attention. */
.hero__signal-point circle { fill: var(--ink); stroke: var(--signal-bright); stroke-width: 1.75; opacity: 0; transition: opacity 500ms var(--ease); }
.hero__signal.is-revealed .hero__signal-point circle { opacity: 1; }
.hero__signal.is-revealed .hero__signal-point:nth-child(2) circle { transition-delay: 500ms; }
.hero__signal.is-revealed .hero__signal-point:nth-child(3) circle { transition-delay: 850ms; }
@media (max-width: 767px) {
  .hero__signal { opacity: 0.28; }
  .hero__signal-point { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .hero__signal-path, .hero__signal-point circle { transition: none !important; opacity: 1 !important; stroke-dashoffset: 0 !important; }
}

.hero .statement { padding-top: 72px; padding-bottom: 88px; border-top: 1px solid var(--line); position: relative; z-index: 1; }
@media (min-width: 1024px) { .hero .statement { padding-top: 96px; padding-bottom: 120px; } }
.statement__quote { font-family: var(--font-display); font-size: clamp(1.75rem, 1.3rem + 2vw, 2.875rem); font-weight: 800; line-height: 1.18; letter-spacing: -0.015em; color: var(--text); max-width: 780px; }
.statement__support { margin-top: 22px; font-size: 1.0625rem; line-height: 1.65; color: var(--text-soft); max-width: 56ch; }

/* =================================================================
   The system diagram (chapter 2) — not a hub-and-spoke map. Six
   systems start scattered, tilted and out of rhythm with each other;
   scrolling into view lets them settle into one calm cluster around
   the WaveIT mark. The idea is legible before the supporting copy is
   read: fragmented, then gathered, then still.

   Positioned by percentage (top/left custom properties per fragment,
   set inline in the markup), never hard-coded pixels, so nothing can
   collide at any width — the two states (--x0/--y0/--r0 scattered,
   --x1/--y1/--r1 settled) are just two coordinate pairs the browser
   tweens between. Mobile gets its own composition below rather than
   a squashed version of this one: see the max-width:699px block.
   ================================================================= */
.gather { position: relative; padding: 8px 0 72px; z-index: 1; }
@media (min-width: 1024px) { .gather { padding: 24px 0 96px; } }

.gather__field { position: relative; max-width: 880px; height: clamp(360px, 48vw, 560px); margin: 0 auto; }

.gather__frag {
  position: absolute; left: var(--x0); top: var(--y0);
  transform: translate(-50%, -50%) rotate(var(--r0)) scale(0.9);
  opacity: 0.5;
  display: flex; flex-direction: column; align-items: center; gap: 10px; width: 128px;
  transition: left 1000ms var(--ease), top 1000ms var(--ease), transform 1000ms var(--ease), opacity 800ms var(--ease);
  transition-delay: var(--d, 0ms);
}
.gather.is-active .gather__frag {
  left: var(--x1); top: var(--y1);
  transform: translate(-50%, -50%) rotate(var(--r1)) scale(1);
  opacity: 1;
}
.gather__icon {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 42px; height: 42px; border-radius: 50%;
  background: var(--paper-2); color: var(--signal-deep);
  border: 1.5px solid var(--line-on-paper); box-shadow: 0 8px 18px rgba(20,33,61,0.08);
}
.gather__icon svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 1.6; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.gather__label { font-size: 0.8125rem; font-weight: 600; color: var(--ink-text-soft); white-space: nowrap; text-align: center; }

.gather__core {
  position: absolute; left: 50%; top: 84%; transform: translate(-50%, -50%);
  text-align: center; opacity: 0; transition: opacity 700ms var(--ease) 950ms, transform 700ms var(--ease) 950ms;
}
.gather.is-active .gather__core { opacity: 1; }
.gather__core-glow {
  position: absolute; left: 50%; top: 50%; width: 220px; height: 220px;
  transform: translate(-50%, -50%) scale(0.4); border-radius: 50%; z-index: -1;
  background: radial-gradient(circle, var(--signal-soft) 0%, rgba(61,123,255,0) 68%);
  opacity: 0; transition: opacity 900ms var(--ease) 700ms, transform 900ms var(--ease) 700ms;
}
.gather.is-active .gather__core-glow { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.gather__core-mark {
  display: block; font-family: var(--font-display); font-weight: 900; letter-spacing: -0.02em;
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.5rem); color: var(--ink-text);
}
.gather__core-caption { display: block; margin-top: 6px; font-size: 0.9375rem; font-weight: 600; color: var(--ink-text-soft); }

/* Mobile: a compact two-column field instead of the desktop canvas.
   Fragments start offset and tilted (alternating sides), settle into
   an even grid, then the WaveIT mark resolves beneath them. */
@media (max-width: 699px) {
  .gather { padding-top: 36px; padding-bottom: 48px; }
  .gather__field {
    height: auto; max-width: 320px; padding: 0 12px;
    display: grid; grid-template-columns: 1fr 1fr; gap: 26px 12px;
  }
  .gather__frag {
    position: static; left: auto !important; top: auto !important; width: auto;
    transform: translate(var(--jx, 0), var(--jy, 0)) rotate(var(--r0)) scale(0.94); opacity: 0.5;
    transition: transform 800ms var(--ease), opacity 700ms var(--ease);
  }
  .gather__frag:nth-child(odd) { --jx: -14px; --jy: -10px; }
  .gather__frag:nth-child(even) { --jx: 14px; --jy: 10px; }
  .gather.is-active .gather__frag { transform: none; opacity: 1; }
  .gather__core { position: relative; left: auto; top: auto; grid-column: 1 / -1; margin-top: 22px; transform: none; }
  .gather__core-glow { width: 170px; height: 170px; }
}

@media (prefers-reduced-motion: reduce) {
  .gather__frag, .gather__core, .gather__core-glow { transition: none !important; }
}

.tree { position: relative; padding-left: 40px; margin-top: 8px; }
.tree__trunk { position: absolute; left: 12px; top: 0; bottom: 26px; width: 2px; background: var(--signal); opacity: 0.4; }
.tree__node { position: relative; display: flex; align-items: center; gap: 14px; padding: 13px 0; opacity: 0; transform: translateX(-8px); transition: opacity 550ms var(--ease), transform 550ms var(--ease); }
.tree.is-revealed .tree__node { opacity: 1; transform: translateX(0); }
.tree__node::before { content: ""; position: absolute; left: -28px; top: 50%; width: 20px; height: 2px; background: var(--signal); opacity: 0.4; }
.tree__label { font-size: 1rem; font-weight: 600; color: var(--ink-text); }

.tree--compact { padding-left: 30px; }
.tree--compact .tree__trunk { left: 8px; bottom: 18px; }
.tree--compact .tree__node { padding: 8px 0; }
.tree--compact .tree__node::before { left: -20px; width: 14px; }
.tree--compact .tree__label { font-size: 0.875rem; font-weight: 500; color: var(--text-soft); }

.service-index { border-top: 1px solid var(--line-on-paper); }
.service-index__row {
  display: grid; grid-template-columns: 44px 1fr; gap: 8px 20px; padding: 30px 16px; margin: 0 -16px;
  border-bottom: 1px solid var(--line-on-paper); border-radius: var(--radius);
  transition: background 260ms var(--ease), transform 260ms var(--ease);
}
.service-index__row:hover { background: var(--pale-2); transform: translateX(4px); }
.service-index__number { font-family: var(--font-mono); font-size: 0.9375rem; font-weight: 700; color: var(--signal-deep); }
.service-index__name { font-size: 1.4375rem; font-weight: 800; letter-spacing: -0.01em; }
.service-index__desc { margin-top: 8px; font-size: 1rem; line-height: 1.6; color: var(--ink-text-soft); max-width: 60ch; }

.capability-line { margin-top: 36px; font-size: 0.9375rem; line-height: 1.6; color: var(--ink-text-mute); max-width: 68ch; }

/* =================================================================
   Performance chapter — no graphs, no dials, no invented data. Two
   documented facts carry the entire section: 24/7 monitoring and a
   15-minute target response. The numerals ARE the art direction —
   large enough to be the composition, not stat cards labelled with
   them. One shared line motif connects the two beats but animates
   differently for each, matching what each fact actually means:
   24/7 breathes continuously (a slow, unending pulse — the point is
   that it never stops); 15 snaps in once, fast and decisive, the
   instant it's scrolled into view (the point is speed).
   ================================================================= */
.pace {
  margin-top: 56px; position: relative; z-index: 1;
  display: grid; gap: 56px;
}
@media (min-width: 768px) { .pace { margin-top: 80px; grid-template-columns: 1fr auto 1fr; gap: 0; align-items: start; } }

.pace__beat { max-width: 34ch; }
@media (min-width: 768px) { .pace__beat--always { padding-right: 56px; } .pace__beat--fast { padding-left: 56px; } }

.pace__divider { display: none; }
@media (min-width: 768px) {
  .pace__divider { display: block; width: 1px; background: rgba(255,255,255,0.18); align-self: stretch; }
}
@media (max-width: 767px) { .pace__beat--fast { padding-top: 40px; border-top: 1px solid rgba(255,255,255,0.16); } }

.pace__statement { font-size: 1.1875rem; font-weight: 700; letter-spacing: -0.005em; line-height: 1.35; color: #fff; }

.pace__figure { display: flex; align-items: baseline; gap: 10px; margin-top: 28px; }
.pace__num { font-family: var(--font-display); font-size: clamp(4rem, 2.6rem + 7vw, 8rem); font-weight: 900; line-height: 0.85; letter-spacing: -0.035em; color: #fff; }
.pace__slash { font-family: var(--font-display); font-size: clamp(2rem, 1.4rem + 2.4vw, 3.75rem); font-weight: 300; color: rgba(255,255,255,0.42); }
.pace__unit { font-size: clamp(1.125rem, 0.9rem + 0.7vw, 1.5rem); font-weight: 700; color: rgba(255,255,255,0.7); text-transform: lowercase; }

.pace__detail { margin-top: 22px; font-size: 0.9375rem; line-height: 1.55; color: rgba(255,255,255,0.86); }

/* 24/7 — a slow, continuous breathing line under the numeral. The
   one perpetual animation in this section, because the entire point
   of this fact is that it never stops; a looping motion is motivated
   by the content, not decoration for its own sake. */
.pace__beat--always .pace__figure::after {
  content: ""; display: block; align-self: center; flex: 1 1 auto; height: 2px; margin-left: 4px;
  background: linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,0.75) 48%, rgba(255,255,255,0) 100%);
  animation: pace-breathe 5.5s ease-in-out infinite;
}
@keyframes pace-breathe { 0%, 100% { opacity: 0.32; transform: scaleX(0.72); } 50% { opacity: 1; transform: scaleX(1); } }

/* 15 min — the numeral itself snaps in once, fast, the moment it
   scrolls into view (see the [data-pace-fast] IntersectionObserver
   in script.js): a short, decisive motion standing for speed, never
   repeating once it has landed. */
.pace__beat--fast .pace__figure {
  opacity: 0; transform: scale(0.82) translateY(6px); transform-origin: left bottom;
  transition: opacity 380ms cubic-bezier(0.34, 1.56, 0.64, 1), transform 380ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.pace__beat--fast.is-active .pace__figure { opacity: 1; transform: scale(1) translateY(0); }
.pace__beat--fast .pace__num { position: relative; }
.pace__beat--fast .pace__num::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -0.16em; height: 3px; background: var(--signal-bright);
  transform: scaleX(0); transform-origin: left; transition: transform 320ms var(--ease) 260ms;
}
.pace__beat--fast.is-active .pace__num::after { transform: scaleX(1); }

@media (prefers-reduced-motion: reduce) {
  .pace__beat--always .pace__figure::after { animation: none !important; opacity: 0.7; transform: none; }
  .pace__beat--fast .pace__figure, .pace__beat--fast .pace__num::after { transition: none !important; opacity: 1 !important; transform: none !important; }
}

.standard-line { margin-top: 96px; padding-top: 40px; border-top: 1px solid rgba(255,255,255,0.18); display: grid; gap: 24px; position: relative; z-index: 1; }
@media (min-width: 640px) { .standard-line { grid-template-columns: 1fr 1fr; gap: 28px 40px; } }
.standard-line > div { padding: 14px 16px; margin: -14px -16px; border-radius: var(--radius); transition: background 260ms var(--ease); }
.standard-line > div:hover { background: rgba(255,255,255,0.06); }
.standard-line dt { font-size: 0.9375rem; font-weight: 700; color: #fff; }
.standard-line dd { margin-top: 4px; font-size: 0.875rem; line-height: 1.5; color: rgba(255,255,255,0.82); }

/* =================================================================
   Sectors — one flowing sentence
   ================================================================= */
.sectors { padding: 72px 0; position: relative; }
.sectors__sentence { font-size: clamp(1.25rem, 1rem + 1.1vw, 1.75rem); line-height: 1.5; max-width: 62ch; color: var(--text-soft); position: relative; z-index: 1; }
.sectors__sentence strong { color: var(--text); font-weight: 700; }
.chapter--pale .sectors__sentence { color: var(--pale-text-soft); }
.chapter--pale .sectors__sentence strong { color: var(--pale-text); }

/* =================================================================
   Experience — sticky story stack
   ================================================================= */
.experience { padding-top: 64px; }
@media (min-width: 1024px) { .experience { padding-top: 88px; } }
.experience__head { margin-bottom: 40px; position: relative; z-index: 2; }
.experience .section__lead { max-width: 60ch; }
.experience__preview {
  margin-top: 28px; padding-top: 24px; border-top: 1px solid var(--line);
  font-size: 0.9375rem; color: var(--text-mute); letter-spacing: 0.01em;
}
.experience__preview span { color: var(--signal); margin: 0 10px; }

.stack { position: relative; }

.story {
  position: sticky;
  top: var(--header-h);
  min-height: 100vh;
  display: flex;
  align-items: center;
  background: var(--ink-2);
  border-top: 1px solid var(--line);
}
.story:nth-child(even) { background: var(--ink-3); }

.story__inner { width: 100%; display: grid; gap: 40px; padding-top: 56px; padding-bottom: 56px; position: relative; }
@media (min-width: 900px) { .story__inner { grid-template-columns: 1fr 1.1fr; align-items: center; gap: 64px; } }

/* Mobile gets its own composition, not the desktop stack narrowed:
   four full-height sticky panels is too much scroll commitment on a
   phone, so below 768px every story reverts to plain document flow
   at a compact height — the craft (each project's own figure) stays,
   the scroll-jacking doesn't. */
@media (max-width: 767px) {
  .story { position: static; min-height: 0; display: block; }
  .story__inner { padding-top: 36px; padding-bottom: 40px; gap: 22px; }
  /* Text first, figure second: the credential is what a phone
     reader scans for, the figure is supporting detail. */
  .story__content { order: 1; }
  .story__figure { order: 2; min-height: 0; padding-top: 20px; border-top: 1px solid var(--line); }
  .story__figure--large { min-height: 0; }
}

.story__index {
  display: block; margin-bottom: 20px;
  font-family: var(--font-mono); font-weight: 700; font-size: 0.8125rem; letter-spacing: 0.08em;
  color: var(--signal-bright);
}
.story__index::after { content: " / 04"; color: var(--text-mute); }

.story__figure { position: relative; z-index: 1; min-height: 160px; display: flex; flex-direction: column; justify-content: center; }
.story__figure--large { min-height: 260px; }
.story__figure-caption { margin-top: 18px; font-size: 0.875rem; letter-spacing: 0.01em; color: var(--text-mute); }

.story__content { position: relative; z-index: 1; }
.story__logo { width: 112px; height: 112px; background: #fff; border-radius: var(--radius-lg); display: flex; align-items: center; justify-content: center; padding: 18px; margin-bottom: 24px; box-shadow: 0 20px 40px rgba(0,0,0,0.25); }
.story__logo img { width: 100%; height: 100%; object-fit: contain; }

.story__org { font-family: var(--font-display); font-size: 1.75rem; font-weight: 800; letter-spacing: -0.015em; }
.story__note { font-weight: 400; color: var(--text-mute); font-size: 0.8em; }
.story__role { margin-top: 6px; font-size: 0.9375rem; letter-spacing: 0.01em; color: var(--signal-bright); font-weight: 600; }
.story__desc { margin-top: 16px; font-size: 1.0625rem; line-height: 1.6; color: var(--text-soft); max-width: 52ch; }

/* Story figure — sequence (rollout) */
.sequence { position: relative; height: 4px; background: var(--signal-soft); border-radius: 2px; display: flex; align-items: center; justify-content: space-between; }
.sequence__line { display: none; }
.sequence__dot {
  width: 14px; height: 14px; border-radius: 50%; background: var(--ink-2);
  border: 2px solid var(--line-strong); flex-shrink: 0; transition: background 400ms var(--ease), border-color 400ms var(--ease), transform 400ms var(--ease);
}
.sequence.is-active .sequence__dot { animation: dot-pulse 900ms var(--ease) forwards; }
.sequence.is-active .sequence__dot:nth-child(2) { animation-delay: 0ms; }
.sequence.is-active .sequence__dot:nth-child(3) { animation-delay: 140ms; }
.sequence.is-active .sequence__dot:nth-child(4) { animation-delay: 280ms; }
.sequence.is-active .sequence__dot:nth-child(5) { animation-delay: 420ms; }
.sequence.is-active .sequence__dot:nth-child(6) { animation-delay: 560ms; }
@keyframes dot-pulse { to { background: var(--signal); border-color: var(--signal); transform: scale(1.15); } }

/* Story figure — migrate (News Corp) */
.migrate { display: flex; align-items: center; }
.migrate__node {
  font-family: var(--font-sans); font-size: 0.875rem; font-weight: 600; letter-spacing: 0;
  padding: 10px 14px; border: 1px solid var(--line-strong); border-radius: var(--radius); color: var(--text-soft); flex-shrink: 0;
}
.migrate__node--mid { color: var(--signal-bright); border-color: var(--signal-soft); }
.migrate__line { position: relative; flex: 1 1 auto; height: 2px; background: var(--signal-soft); margin: 0 8px; }
.migrate__dot { position: absolute; top: 50%; left: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--signal); transform: translate(-50%, -50%); opacity: 0; }
.migrate.is-active .migrate__dot { animation: dot-travel 1600ms var(--ease) 300ms forwards; }
@keyframes dot-travel { 0% { left: 0; opacity: 1; } 100% { left: 100%; opacity: 1; } }

/* Story figure — converge (Westpac). One of the strongest visual
   moments in Experience: three real, recognisable brand marks
   converging into one environment, at real scale — not a row of
   favicon-sized thumbnails above a heading. */
.converge { display: flex; flex-direction: column; align-items: center; gap: 0; }
.converge__leaves { display: flex; gap: 20px; flex-wrap: wrap; justify-content: center; }
.converge__leaf {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  opacity: 0; transform: translateY(-14px); transition: opacity 550ms var(--ease), transform 550ms var(--ease);
}
.converge__leaf img {
  width: 96px; height: 96px; object-fit: contain; background: #fff;
  border-radius: var(--radius-lg); padding: 18px; box-shadow: 0 20px 40px rgba(0,0,0,0.28);
}
.converge__leaf b { font-size: 0.9375rem; font-weight: 600; color: var(--text-soft); }
.converge.is-active .converge__leaf { opacity: 1; transform: translateY(0); }
.converge.is-active .converge__leaf:nth-child(2) { transition-delay: 110ms; }
.converge.is-active .converge__leaf:nth-child(3) { transition-delay: 220ms; }
.converge__trunk { width: 2px; height: 56px; background: var(--signal); opacity: 0.6; transform: scaleY(0); transform-origin: top; transition: transform 550ms var(--ease) 450ms; }
.converge.is-active .converge__trunk { transform: scaleY(1); }
.converge__hub {
  font-size: 1.0625rem; font-weight: 700; letter-spacing: 0.01em;
  padding: 14px 28px; border-radius: 999px; background: var(--signal-deep); color: #fff;
  opacity: 0; transform: scale(0.9); transition: opacity 450ms var(--ease) 750ms, transform 450ms var(--ease) 750ms;
}
.converge.is-active .converge__hub { opacity: 1; transform: scale(1); }
@media (min-width: 640px) { .converge__leaf img { width: 116px; height: 116px; } }

/* =================================================================
   Testimonials — data-driven, hidden until populated. Built as a
   stacked deck (the same --stack-i sticky pattern as the Experience
   stories) so real quotes, once added, feel substantial rather than
   three generic cards. See TESTIMONIALS in script.js for the shape.
   ================================================================= */
.testimonials { padding: 88px 0; overflow: hidden; }
@media (min-width: 1024px) { .testimonials { padding: 132px 0; } }
.testimonials__stack { position: relative; margin-top: 56px; }
.testimonial {
  position: sticky; top: calc(var(--header-h) + 24px);
  background: var(--ink-2); padding: 56px 44px; border-radius: var(--radius-lg);
  border: 1px solid var(--line); box-shadow: 0 32px 64px rgba(0,0,0,0.35);
  margin-bottom: 24px;
  transform: translateY(calc(var(--stack-i, 0) * 4px)) scale(calc(1 - var(--stack-i, 0) * 0.02));
}
.testimonial__quote { font-family: var(--font-display); font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem); font-weight: 700; line-height: 1.2; letter-spacing: -0.015em; }
.testimonial__byline { margin-top: 28px; display: flex; align-items: center; gap: 14px; }
.testimonial__logo { width: 68px; height: 68px; background: #fff; border-radius: var(--radius-lg); object-fit: contain; padding: 12px; box-shadow: 0 12px 28px rgba(0,0,0,0.3); }
.testimonial__photo { width: 68px; height: 68px; border-radius: 50%; object-fit: cover; }
.testimonial__name { font-weight: 700; }
.testimonial__role { font-size: 0.875rem; color: var(--text-mute); }

/* =================================================================
   Contact — a confident close, not another set-piece. Quieter than
   the chapters before it on purpose: no ghost wordmark, no eyebrow,
   just the ask, the direct details, and room to breathe.
   ================================================================= */
.contact { position: relative; overflow: hidden; padding: 112px 0 96px; }
@media (min-width: 1024px) { .contact { padding: 160px 0 140px; } }

.contact__inner { position: relative; z-index: 1; display: grid; gap: 48px; }
@media (min-width: 1024px) { .contact__inner { grid-template-columns: 1.2fr 1fr; gap: 64px; align-items: center; } }
.contact__heading { font-family: var(--font-display); font-size: clamp(2.25rem, 1.5rem + 3vw, 4rem); font-weight: 800; line-height: 1.03; letter-spacing: -0.02em; color: #fff; max-width: 15ch; }
.contact__lead { margin-top: 20px; font-size: 1.125rem; line-height: 1.6; color: rgba(255,255,255,0.82); max-width: 44ch; }
.contact__intro .cta { margin-top: 40px; }

.contact__card {
  position: relative; padding: 40px 36px; border-radius: var(--radius-lg);
  background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14);
  backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
  box-shadow: 0 32px 64px rgba(3,7,25,0.35), inset 0 1px 0 rgba(255,255,255,0.12);
}
.contact__card-label { font-size: 0.9375rem; font-weight: 600; letter-spacing: 0.01em; color: rgba(255,255,255,0.65); margin-bottom: 20px; }
.contact__direct { display: flex; flex-direction: column; gap: 14px; font-size: 1.1875rem; font-weight: 600; }
.contact__direct a { color: #fff; border-bottom: 1px solid rgba(255,255,255,0.35); transition: border-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease); padding-bottom: 2px; }
.contact__direct a:hover { border-color: #fff; opacity: 0.85; }
.contact__direct li:last-child { color: rgba(255,255,255,0.72); font-weight: 400; font-size: 1rem; }
.contact__card-note { margin-top: 24px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.14); font-size: 0.9375rem; line-height: 1.5; color: rgba(255,255,255,0.68); }

@media (max-width: 640px) {
  .contact__card { padding: 32px 26px; }
}

/* =================================================================
   Lead modal — "Start a conversation". Ink surface, not a white
   rectangle; the panel draws in from a horizontal line (echoing the
   rail's scaleY draw) and carries its own rail down the left edge.
   ================================================================= */
.modal[hidden] { display: none; }
.modal { position: fixed; inset: 0; z-index: 800; display: flex; align-items: center; justify-content: center; padding: 24px; }
.modal__backdrop { position: absolute; inset: 0; background: rgba(5, 7, 12, 0.7); backdrop-filter: blur(6px); opacity: 0; transition: opacity 320ms var(--ease); }
.modal.is-active .modal__backdrop { opacity: 1; }

.modal__panel {
  position: relative; z-index: 1; width: 100%; max-width: 560px; max-height: min(88vh, 780px);
  overflow-y: auto; background: var(--ink); border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius-lg); box-shadow: 0 60px 120px rgba(0,0,0,0.55);
  padding: 44px 40px 40px;
  transform: scaleY(0.06) scale(0.97); opacity: 0; transform-origin: 50% 50%;
  transition: transform 420ms var(--ease), opacity 260ms var(--ease);
}
.modal.is-active .modal__panel { transform: scaleY(1) scale(1); opacity: 1; }

.modal__rail {
  position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--signal);
  transform: scaleY(0); transform-origin: top; transition: transform 700ms var(--ease) 200ms;
}
.modal.is-active .modal__rail { transform: scaleY(1); }

.modal__close {
  position: absolute; top: 20px; right: 20px; width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center; color: var(--text-soft);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.modal__close:hover { background: rgba(255,255,255,0.08); color: var(--text); }

.modal__heading { margin-top: 6px; font-family: var(--font-display); font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.125rem); font-weight: 800; letter-spacing: -0.015em; }
.modal__lead { margin-top: 10px; font-size: 1rem; line-height: 1.55; color: var(--text-soft); max-width: 42ch; }
.modal__body form { margin-top: 28px; }

.modal__success { text-align: center; padding: 24px 0 8px; }
.modal__success[hidden] { display: none; }
.modal__success-mark {
  display: block; width: 52px; height: 52px; margin: 0 auto 22px; border-radius: 50%;
  background: var(--signal-soft); position: relative;
}
.modal__success-mark::after {
  content: ""; position: absolute; left: 50%; top: 46%; width: 18px; height: 10px;
  border-left: 2.5px solid var(--signal-bright); border-bottom: 2.5px solid var(--signal-bright);
  transform: translate(-50%, -60%) rotate(-45deg);
}
.modal__success-heading { font-size: 1.375rem; font-weight: 800; }
.modal__success-body { margin-top: 10px; font-size: 0.9375rem; line-height: 1.55; color: var(--text-soft); max-width: 40ch; margin-inline: auto; }
.modal__success .btn { margin-top: 26px; }

@media (max-width: 640px) {
  .modal { padding: 0; align-items: flex-end; }
  .modal__panel { max-width: none; max-height: 92vh; border-radius: var(--radius-lg) var(--radius-lg) 0 0; padding: 36px 22px 28px; }
}
@media (prefers-reduced-motion: reduce) {
  .modal__backdrop, .modal__panel, .modal__rail, .modal__close { transition: none !important; }
  .modal.is-active .modal__panel { transform: none !important; }
}

.contact-form { display: flex; flex-direction: column; gap: 20px; }
.contact-form__status { font-size: 0.9375rem; color: var(--signal-bright); min-height: 0; }
.contact-form__status:empty { display: none; }
.field { display: flex; flex-direction: column; gap: 8px; }
.field__label { font-family: var(--font-sans); font-size: 0.875rem; letter-spacing: 0; font-weight: 600; color: var(--text-soft); }
.field__optional { font-weight: 400; text-transform: none; color: var(--text-mute); }
.field__input { background: var(--ink-2); border: 1px solid var(--line-strong); border-radius: var(--radius); padding: 13px 14px; color: var(--text); font-family: var(--font-sans); transition: border-color var(--dur-fast) var(--ease); }
.field__input::placeholder { color: var(--text-mute); }
.field__input:focus { border-color: var(--signal-bright); }
.field__input--textarea { resize: vertical; min-height: 96px; }
.field--invalid .field__input { border-color: var(--error); }
.field__error { font-size: 0.8125rem; color: var(--error); min-height: 0; }
.field__error:empty { display: none; }

/* =================================================================
   Footer — a closing statement first, then a properly weighted
   three-column base, not three lines of small grey text.
   ================================================================= */
.site-footer { background: var(--ink-deep); color: var(--text); padding-top: 96px; border-top: 1px solid var(--line); position: relative; overflow: hidden; z-index: 1; }
@media (min-width: 1024px) { .site-footer { padding-top: 132px; } }

.site-footer__statement-wrap { position: relative; z-index: 1; padding-bottom: 72px; }
.site-footer__statement {
  font-family: var(--font-display); font-weight: 800; letter-spacing: -0.02em;
  font-size: clamp(1.75rem, 1.2rem + 2.6vw, 3.25rem); line-height: 1.15; max-width: 18ch; color: var(--text);
}

.site-footer__inner { position: relative; z-index: 1; display: grid; gap: 40px; padding-top: 40px; padding-bottom: 48px; border-top: 1px solid var(--line); }
@media (min-width: 768px) { .site-footer__inner { grid-template-columns: 1.2fr 1fr 1fr; gap: 24px; } }
.wordmark--footer { font-size: 1.75rem; }
.site-footer__tagline { margin-top: 14px; color: var(--text-soft); font-size: 1rem; max-width: 28ch; }
.site-footer__col-label { font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.02em; color: var(--text-mute); margin-bottom: 16px; }
.site-footer__nav ul { display: flex; flex-direction: column; gap: 12px; }
.site-footer__nav a { font-family: var(--font-sans); font-weight: 600; color: var(--text-soft); font-size: 1.0625rem; letter-spacing: 0; transition: color var(--dur-fast) var(--ease); }
.site-footer__nav a:hover { color: var(--text); }
.site-footer__contact { display: flex; flex-direction: column; gap: 12px; font-size: 1.0625rem; font-weight: 600; }
.site-footer__contact a { color: var(--text-soft); transition: color var(--dur-fast) var(--ease); }
.site-footer__contact a:hover { color: var(--text); }
.site-footer__contact p { color: var(--text-mute); font-size: 0.9375rem; font-weight: 400; }
.site-footer__legal { position: relative; z-index: 1; border-top: 1px solid var(--line); padding-top: 22px; padding-bottom: 22px; font-size: 0.8125rem; letter-spacing: 0.01em; color: var(--text-mute); display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap; }

/* =================================================================
   Mobile pass (<=767px) — desktop is untouched. Desktop is
   immersive; mobile is focused: one dominant element at a time,
   tighter and more deliberate section rhythm, calmer motion. Sits at
   the end of the file so it wins over the base rules it adapts.
   ================================================================= */
@media (max-width: 767px) {
  :root { --header-h: 60px; }
  .wordmark { font-size: 1.25rem; }
  .menu-toggle { width: 44px; height: 44px; margin-right: -8px; }

  /* Motion: shorter travel, quicker settle, so reveals guide the eye
     without adding movement while someone is reading. */
  [data-reveal-fade] { transform: translateY(10px); transition-duration: 520ms; }

  /* Headings: balanced line breaks, calmer scale */
  .section__heading, .contact__heading, .statement__quote, .site-footer__statement { text-wrap: balance; }
  .section { padding: 64px 0; }
  .section__header { margin-bottom: 32px; }
  .section__lead { margin-top: 14px; font-size: 1rem; }

  /* Hero: proposition and one action first; the line is atmosphere */
  .hero { padding-top: 0; }
  .hero__inner { padding-top: 44px; padding-bottom: 48px; }
  .hero__meta { font-size: 0.6875rem; letter-spacing: 0.1em; margin-bottom: 16px; }
  .hero__meta span { margin: 0 6px; }
  .hero__foot { margin-top: 28px; gap: 22px; }
  .hero__lead { font-size: 1.0625rem; flex-basis: 100%; }
  .hero__actions { gap: 0 16px; align-items: center; margin-top: 0; }
  .hero__actions .btn--ghost {
    border-color: transparent; background: none; min-height: 44px; padding: 10px 2px;
    font-size: 0.9375rem; color: var(--text-soft);
    text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 5px;
  }
  .hero .hero__signal.is-revealed { opacity: 0.3; }
  .hero .statement { padding-top: 48px; padding-bottom: 56px; }
  .statement__quote { font-size: 1.625rem; line-height: 1.22; }
  .statement__support { font-size: 1rem; margin-top: 16px; }

  /* System */
  .service-index__row { grid-template-columns: 28px 1fr; gap: 6px 14px; padding: 22px 0; margin: 0; }
  .service-index__row:hover { background: none; transform: none; }
  .service-index__name { font-size: 1.25rem; }
  .service-index__desc { font-size: 0.9375rem; margin-top: 6px; }
  .capability-line { margin-top: 26px; font-size: 0.875rem; }

  /* Performance */
  .pace { margin-top: 40px; gap: 36px; }
  .pace__beat { max-width: none; }
  .pace__detail { max-width: 34ch; }
  .pace__beat--fast { padding-top: 32px; }
  .pace__statement { font-size: 1.0625rem; }
  .pace__figure { margin-top: 20px; }
  .pace__detail { margin-top: 16px; }
  .standard-line { margin-top: 52px; padding-top: 28px; gap: 18px; }
  .standard-line > div { margin: 0; padding: 0; }
  .standard-line > div:hover { background: none; }

  /* Sectors */
  .sectors { padding: 52px 0; }
  .sectors__sentence { font-size: 1.125rem; line-height: 1.55; }

  /* Experience */
  .experience { padding-top: 56px; }
  .experience__head { margin-bottom: 20px; }
  .experience__preview { margin-top: 18px; padding-top: 16px; font-size: 0.8125rem; line-height: 1.7; }
  .story__index { margin-bottom: 14px; }
  .story__logo { width: 56px; height: 56px; padding: 10px; margin-bottom: 14px; box-shadow: none; }
  /* Logo sits beside the name and role instead of stacking above
     them — the credential reads in one glance and each story is
     roughly a third shorter. Westpac has no logo tile, so it keeps
     the plain stack. */
  .story__content:has(.story__logo) { display: grid; grid-template-columns: 56px 1fr; column-gap: 14px; align-items: center; }
  .story__content:has(.story__logo) .story__index { grid-column: 1 / -1; }
  .story__content:has(.story__logo) .story__logo { grid-row: 2 / 4; margin: 0; align-self: start; }
  .story__content:has(.story__logo) .story__org { grid-column: 2; grid-row: 2; align-self: end; line-height: 1.15; }
  .story__note { display: block; margin-top: 3px; font-size: 0.8125rem; }
  .story__content:has(.story__logo) .story__role { grid-column: 2; grid-row: 3; margin-top: 4px; align-self: start; }
  .story__content:has(.story__logo) .story__desc { grid-column: 1 / -1; margin-top: 16px; }
  .story__org { font-size: 1.375rem; }
  .story__role { font-size: 0.875rem; }
  .story__desc { font-size: 0.9375rem; margin-top: 12px; }
  .tree--compact .tree__node { padding: 6px 0; }
  .converge__leaves { gap: 12px; }
  .converge__leaf { gap: 8px; }
  .converge__leaf img { width: 64px; height: 64px; padding: 11px; box-shadow: 0 10px 22px rgba(0,0,0,0.25); }
  .converge__leaf b { font-size: 0.75rem; }
  .converge__trunk { height: 26px; }
  .converge__hub { font-size: 0.9375rem; padding: 10px 22px; }

  /* Contact */
  .contact { padding: 72px 0 64px; }
  .contact__inner { gap: 32px; }
  .contact__heading { font-size: 2.125rem; line-height: 1.08; }
  .contact__lead { font-size: 1rem; margin-top: 14px; }
  .contact__intro .cta { margin-top: 26px; }
  .contact__card { padding: 24px 22px; }
  .contact__card-label { margin-bottom: 14px; }
  .contact__direct { gap: 10px; font-size: 1.0625rem; }

  /* Footer */
  .site-footer { padding-top: 60px; }
  .site-footer__statement-wrap { padding-bottom: 36px; }
  .site-footer__statement { font-size: 1.625rem; max-width: 22ch; }
  .site-footer__inner { gap: 26px; padding-top: 30px; padding-bottom: 32px; }
  .site-footer__statement { font-weight: 700; font-size: 1.5rem; }
  .site-footer__tagline { margin-top: 10px; font-size: 0.9375rem; }
  .site-footer__nav .site-footer__col-label { display: none; }
  .site-footer__nav ul { flex-direction: row; flex-wrap: wrap; gap: 4px 18px; }
  .site-footer__nav a { font-size: 0.9375rem; display: inline-block; padding: 8px 0; }
  .site-footer__col-label { margin-bottom: 10px; }
  .site-footer__contact { gap: 6px; }
  .site-footer__contact a { padding: 6px 0; }
  .site-footer__legal p:last-child { display: none; }
}
