/* ═══════════════════════════════════════════════════════════════════════
   nsquareai v2

   Grammar: overture, then chapters. Movement I is cinema, Movement II is a
   document, one hard seam between them.

   The ground is a property of the chapter, never decoration. Every ground
   change is a hard cut at a chapter boundary: no gap, no rule, no radius,
   no gradient. The colour change is the divider.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Tokens ─────────────────────────────────────────────────────────── */

:root {
  /* Ground ladder. Every value is the brand hue at a different lightness. */
  --void:        #0A1719;  /* overture only, deepest */
  --ink-ground:  #0D1F22;  /* chapter dark */
  --ink-lifted:  #122A2E;  /* only if two darks ever touch */
  --teal-ground: #06393E;  /* chapter 4 only */
  --paper:       #F6F8F8;  /* light chapters */
  --surface:     #FFFFFF;  /* lifted on paper */

  /* Accent: one hue (184deg), three lightnesses keyed to the ground.
     A page that hard-cuts between grounds cannot clear 4.5:1 on both with
     a single stop. Brand teal on paper is 2.9:1, which fails even the 3:1
     large-text floor, so paper gets its own stop. */
  --accent-void:  #00A7B3;  /* 6.3:1 on void, 5.8:1 on ink */
  --accent-teal:  #7FE3EA;  /* 8.5:1 on the teal ground */
  --accent-paper: #00727A;  /* 5.3:1 on paper */

  /* Type */
  --font-display: "Newsreader", Georgia, "Times New Roman", serif;
  --font-text:    "Inter", system-ui, -apple-system, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, "SF Mono", monospace;

  --t-overture: clamp(2.5rem, 7.5vw, 6rem);
  --t-chapter:  clamp(2rem, 4vw, 3.25rem);
  --t-quote:    clamp(1.5rem, 2.6vw, 2rem);
  --t-lead:     clamp(1.125rem, 1.6vw, 1.375rem);
  --t-body:     1.125rem;
  --t-small:    0.9375rem;
  --t-label:    0.75rem;
  --t-cite:     0.6875rem;

  /* Space, 4px base */
  --s1: 0.25rem;  --s2: 0.5rem;   --s3: 0.75rem;  --s4: 1rem;
  --s5: 1.5rem;   --s6: 2rem;     --s7: 3rem;     --s8: 4rem;
  --s9: 6rem;     --s10: 8rem;

  --section: clamp(3.5rem, 9vw, 7.5rem);
  --shell:   1280px;
  --measure: 66ch;
  --gutter:  clamp(1.25rem, 4vw, 3rem);

  /* Gaps inside a section. Desktop values equal the literals they replace;
     a phone halves them (see the phone block at the end of the file). */
  --gap-lg: var(--s9);
  --gap-md: var(--s8);

  /* One radius scale, held across the page. Editorial means low. */
  --r-1: 2px;
  --r-2: 4px;
  --r-3: 8px;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --dur-fast: 140ms;
  --dur:      220ms;

  /* z-scale, documented rather than sprinkled */
  --z-canvas: 1;
  --z-copy:   2;
  --z-folio:  40;
  --z-nav:    50;
  --z-grain:  60;
}

/* ── Grounds ────────────────────────────────────────────────────────────
   Redefining --ink on a subtree does NOT re-ink the text under it: `color`
   inherits as a computed value, so text that already resolved on <body>
   keeps the body ink no matter what the section redefines the token to.
   Every page that inverts a ground mid-page hits this and it fails
   silently at about 1.15:1. Hence the explicit `color` on every ground. */

.ground-void {
  background: var(--void);
  --ink: #E6EEEE; --ink-soft: #93A6A8;
  --accent: var(--accent-void);
  --hairline: rgba(230, 238, 238, 0.12);
  color: var(--ink);
}

.ground-ink {
  background: var(--ink-ground);
  --ink: #E6EEEE; --ink-soft: #93A6A8;
  --accent: var(--accent-void);
  --hairline: rgba(230, 238, 238, 0.12);
  color: var(--ink);
}

.ground-teal {
  background: var(--teal-ground);
  --ink: #EAF4F4; --ink-soft: #A8C4C4;
  --accent: var(--accent-teal);
  --hairline: rgba(234, 244, 244, 0.16);
  color: var(--ink);
}

.ground-paper {
  background: var(--paper);
  --ink: #0D1F22; --ink-soft: #4A5A5C;
  --accent: var(--accent-paper);
  --hairline: #DCE3E3;
  color: var(--ink);
}

/* Light text on dark needs compensation: a touch more tracking and, on the
   thinnest sizes, one step more weight. Dark-ground type set with
   light-ground metrics looks thin and blurry, and this is why. */
.ground-void, .ground-ink, .ground-teal { --body-track: 0.004em; }
.ground-paper                           { --body-track: 0; }

/* ── Reset ──────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* No scroll-behavior: smooth. Lenis drives the page by calling
     window.scrollTo() once per frame, and CSS smooth scrolling makes the
     browser run its own easing on top of every one of those 60 writes a
     second. The viewport then trails Lenis's own animated value by more
     than half a second: measured at animatedScroll 93.7px while the real
     scrollY was still 0. That double smoothing is what read as input lag.
     Anchor jumps are handled in scroll.js through lenis.scrollTo instead. */
  caret-color: #00A7B3;
  scrollbar-color: #2A3D40 #0A1719;
}

body {
  margin: 0;
  background: var(--void);
  color: #E6EEEE;
  font-family: var(--font-text);
  font-size: var(--t-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
}

img, svg, canvas { display: block; max-width: 100%; }

/* Browser surfaces. The parts you did not draw still carry the design. */
::selection    { background: #00A7B3; color: #06181B; }
:focus-visible { outline: 2px solid var(--accent, #00A7B3);
                 outline-offset: 3px; border-radius: var(--r-1); }

/* ── Type primitives ────────────────────────────────────────────────── */

.overture-head,
.chapter-head,
.pull {
  font-family: var(--font-display);
  font-weight: 500;
  text-wrap: balance;
  margin: 0;
}

.overture-head {
  font-size: var(--t-overture);
  line-height: 1.06;
  letter-spacing: -0.015em;
  font-variation-settings: "opsz" 72;
}

.chapter-head {
  font-size: var(--t-chapter);
  line-height: 1.12;
  letter-spacing: -0.02em;
  font-variation-settings: "opsz" 48;
  max-width: 26ch;
}

.pull {
  font-size: var(--t-quote);
  font-weight: 400;
  font-style: italic;
  line-height: 1.32;
  letter-spacing: -0.01em;
  font-variation-settings: "opsz" 36;
}

.lead {
  font-size: var(--t-lead);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: 58ch;
  text-wrap: pretty;
  margin: 0;
}

.body {
  font-size: var(--t-body);
  letter-spacing: var(--body-track);
  line-height: 1.55;
  max-width: var(--measure);
  text-wrap: pretty;
  margin: 0;
}

.label {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
}

.cite {
  font-family: var(--font-mono);
  font-size: var(--t-cite);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  margin: 0;
}

.num { font-variant-numeric: tabular-nums; }

/* ── Links ──────────────────────────────────────────────────────────── */

a { color: inherit; }

.link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  transition: text-decoration-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .link:hover { text-decoration-color: var(--accent); }
}
.link:active { transform: translateY(1px); }

/* ── The one CTA ────────────────────────────────────────────────────────
   It appears twice, at the open and the close, and nowhere else. An inline
   link in accent read as a word in a sentence, not as the place to click, so
   the ask needed to be a solid object. A filled surface was the first pass;
   this is the second: a line, lit the way the substrate's own beams are lit,
   a tight core plus a soft wide halo, rather than a flat ring. It borrows
   the field's own colour so it reads as one more thing the substrate is
   doing, not a UI kit component dropped on top of it.

   Both instances sit on the void. --accent-teal on #0A1719 is 9.9:1. The
   core (::before) and the widened halo (::after) are pseudo-elements whose
   opacity is what animates, so nothing here transitions anything but
   transform and opacity. Negative z-index keeps them behind the label. */
.button {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  min-height: 3.25rem;
  padding: 0 var(--s6);
  font-family: var(--font-text);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.005em;
  color: var(--accent-teal);
  text-decoration: none;
  background: transparent;
  border: 1px solid rgba(127, 227, 234, 0.5);
  border-radius: var(--r-2);
  /* The resting glow: the border is already lit, not waiting for a hover to
     switch it on. Core close in, halo wider and fainter, same ratio as the
     substrate's beams (0.95 core weight to 0.35 halo). */
  box-shadow:
    0 0 0 1px rgba(127, 227, 234, 0.18),
    0 0 16px -3px rgba(127, 227, 234, 0.55),
    0 0 40px -10px rgba(0, 167, 179, 0.55);
  transition: transform var(--dur-fast) var(--ease-out);
  -webkit-tap-highlight-color: transparent;
}
.button::before,
.button::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease-out);
}
/* Brightens the line and gives it a faint fill, the core turning up. */
.button::before {
  box-shadow: inset 0 0 0 1px rgba(127, 227, 234, 0.95);
  background: rgba(127, 227, 234, 0.07);
}
/* Widens the halo without moving the line itself. */
.button::after {
  box-shadow:
    0 0 26px -2px rgba(127, 227, 234, 0.8),
    0 0 64px -12px rgba(0, 167, 179, 0.85);
}
@media (hover: hover) and (pointer: fine) {
  .button:hover { transform: translateY(-2px); }
  .button:hover::before,
  .button:hover::after { opacity: 1; }
}
.button:focus-visible {
  outline: 2px solid var(--accent-teal);
  outline-offset: 4px;
  border-radius: var(--r-2);
}
.button:focus-visible::after { opacity: 1; }
.button:active { transform: translateY(1px); transition-duration: 60ms; }
.button:active::before { opacity: 0.7; }
.button:active::after { opacity: 0.55; }

/* ── Layout ─────────────────────────────────────────────────────────── */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Bands butt directly against each other. The colour change is the
   divider, so there is no gap, no rule, no radius, and no shadow here. */
.band {
  padding-block: var(--section);
  position: relative;
}

/* More space above a heading than below it: the gap belongs to the
   boundary between sections, not to the heading-and-body pair. */
.chapter-head + * { margin-top: var(--s5); }

/* Grain. Fixed and pointer-events:none, never on a scrolling container:
   continuous GPU repaints on a scroller destroy mobile frame rate. */
.grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-grain);
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── Vertical rhythm ────────────────────────────────────────────────────
   Named pairs, never one value repeated until everything weighs the same.
   A label and its heading are one unit and sit tight; the break before a
   new unit is generous. Order matters below: equal-specificity rules are
   resolved by source order, so the later rule wins. */

.flow > * + *      { margin-top: var(--s5); }  /* 24  default */
.flow > .label + * { margin-top: var(--s3); }  /* 12  label and heading are a pair */
.flow > .lead + *  { margin-top: var(--s6); }  /* 32  lead closes, body opens */
.flow > * + .pull,
.flow > .pull + *  { margin-top: var(--s7); }  /* 48  a quote needs air both sides */
.flow > * + .cite  { margin-top: var(--s7); }  /* 48  a citation is a block footer */

/* ═══ MOVEMENT I: THE OVERTURE ══════════════════════════════════════════
   A tall section with a pinned stage. Progress runs 0 to 1 across the
   pinned portion; the final viewport-height is the exit, where the paper
   chapter wipes up over a settled frame. The edge of that wipe is hard,
   which is the same edge every later ground change makes.
   ═══════════════════════════════════════════════════════════════════════ */

.overture {
  /* 300vh originally. The settling finishes at p=0.82 and holds after that,
     so the last two thirds of a screen were pure dead scroll. 210vh keeps
     every phase legible and returns most of a screen to the reader. */
  height: 210vh;
  position: relative;
}

.overture__stage {
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow: hidden;
  isolation: isolate;
}

.overture__edges,
.overture__nodes,
.overture__strata {
  position: absolute;
  inset: 0;
  z-index: var(--z-canvas);
}

.overture__edges { width: 100%; height: 100%; }

.overture__nodes,
.overture__strata {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Artifacts. Positioned only by transform, never by top/left. */
.overture__nodes li {
  position: absolute;
  top: 0;
  left: 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  line-height: 1;
  color: var(--ink-soft);
  white-space: nowrap;
  transform: translate3d(-50%, -50%, 0);
}

/* The five strata name themselves only once the field has settled. */
.overture__strata li {
  position: absolute;
  top: 0;
  left: 0;
  color: var(--accent);
  opacity: 0;
  transform: translate3d(0, 0, 0);
}

/* Copy sits low so the field owns the upper frame. Anchored, not centred
   in the frame, which reads filmic rather than like a splash page. */
.overture__copy {
  position: absolute;
  inset: 0;
  z-index: var(--z-copy);
  display: flex;
  align-items: flex-end;
  padding-bottom: clamp(3rem, 9vh, 6rem);
  pointer-events: none;
}
.overture__copy a { pointer-events: auto; }
.overture__copy .overture-head { max-width: 18ch; }

/* Two sentences now, not one: a size down and a wider measure so the pair
   holds in about four lines at desktop instead of six. */
#overture .overture-head {
  font-size: clamp(2.25rem, 5.4vw, 4.5rem);
  max-width: 24ch;
}

/* The answer line. Brand teal, Newsreader italic, on its own line: the
   editorial register the hero was missing. #00A7B3 on the void is 6.3:1. */
.overture-head__act {
  display: block;
  margin-top: 0.14em;
  color: var(--accent);
  font-style: italic;
  font-weight: 400;
}

/* ═══ THE NAV ════════════════════════════════════════════════════════════
   The one piece of fixed chrome on the site, and it replaces what used to
   be here: .ident, the overture's own top-left mark, aligned to the same
   line with the same formula (left: calc(max(...))), now carried by this
   instead so the mark appears once per page, not twice.

   Dark glass regardless of the ground behind it: it needs to read the same
   over void, paper and the live field, so it carries its own --ink/--accent
   rather than inheriting a ground's, the same pattern a ground itself uses.
   Hides on scroll-down, returns on scroll-up or focus (scroll.js), so it
   never fights the overture for the reader's attention on the homepage.

   Liquid glass (scroll.js: navGlass()). Three layers, in paint order,
   because one layer cannot both blur a backdrop and refract the blurred
   result:

     ::before   the frost. backdrop-filter blur + saturate + brightness,
                then a thin smoked tint and a gloss gradient on top of it.
                brightness is what keeps white links legible when the nav
                floats over a paper chapter; the gloss is what stops it
                reading as a black hole over the void ground.
     .nav__lens the refraction. A second backdrop-filter whose backdrop is
                the frost above, displaced by a lens map generated on a
                canvas in navGlass(): neutral through the middle, bending
                hard in the last ~18px before the rim. That squeeze at the
                edge is the whole tell of glass, and it is the thing the
                first attempt (feTurbulence over the panel's own tint) was
                never doing. Added by class, only where it renders right.
     ::after    the specular rim: a masked gradient ring that leans toward
                the cursor via --mx/--mabs, left at 0 (a static ring) when
                navGlass() never attaches the listener, i.e. reduced motion.

   No isolation: isolate here. It makes the element a backdrop root, which
   empties the backdrop its own children are trying to filter: the blur
   then has nothing to blur and the panel renders as a flat slab. position
   + z-index already give it the stacking context it needs. */
.nav {
  position: fixed;
  top: var(--s4);
  left: 50%;
  transform: translateX(-50%);
  /* A wide capsule, centered as a proper header, not a badge pinned to the
     corner. Capped well short of the shell so whitespace stays visible past
     both ends: it should cover the column where the reading content lives,
     never the full width of the page. */
  width: min(680px, calc(100% - var(--gutter) * 4));
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  height: 3.75rem;
  padding-inline: var(--s7);
  /* A capsule, not the page radius. The nav is the one floating object on
     the site and the only place the radius scale is deliberately broken:
     a lens has no corners. */
  border-radius: 999px;
  --ink: #E6EEEE;
  --ink-soft: #C4D4D5;
  --accent: var(--accent-teal);
  --mx: 0;
  --mabs: 0;
  color: var(--ink);
  /* Floating, not stuck on: a tight contact shadow, a wide soft drop, and
     one teal bloom well below the panel. */
  box-shadow:
    0 2px 6px -3px rgba(0, 0, 0, 0.4),
    0 16px 34px -14px rgba(0, 0, 0, 0.55),
    0 30px 60px -32px rgba(0, 167, 179, 0.5);
  transition: transform var(--dur) var(--ease-out), opacity var(--dur) var(--ease-out);
}

.nav::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Real glass barely darkens what's behind it; it blurs and refracts.
     Crushing brightness down to keep text legible is what made this read
     as a painted chip instead of glass. This keeps enough of a dim tint
     that the softer .nav__link color still holds contrast over a bright
     paper ground, while still letting shapes and color from the backdrop
     show through, unlike the near-opaque earlier version. */
  backdrop-filter: blur(20px) saturate(160%) brightness(0.62);
  -webkit-backdrop-filter: blur(20px) saturate(160%) brightness(0.62);
  background:
    linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0.18) 0%,
      rgba(255, 255, 255, 0.06) 40%,
      rgba(255, 255, 255, 0.02) 62%,
      rgba(127, 227, 234, 0.08) 100%
    ),
    rgba(9, 22, 24, 0.17);
  /* Thickness: a lit top edge, light pooling along the bottom inside face,
     and a faint teal bounce through the body. Softer than before so the
     inner edge reads as glass, not a drawn outline. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.2),
    inset 0 -16px 26px -20px rgba(255, 255, 255, 0.32),
    inset 0 0 24px -6px rgba(127, 227, 234, 0.1);
}

.nav__lens {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  display: none;
}

/* Chromium only: Firefox renders feDisplacementMap inside backdrop-filter
   wrong, and Safari ignores url() filters there entirely. Both keep the
   frost and the rim, which still read as glass; navGlass() adds this class
   only after CSS.supports confirms the browser will draw it. */
.nav--lens .nav__lens {
  display: block;
  backdrop-filter: url(#nav-lens) blur(1px) saturate(150%);
  -webkit-backdrop-filter: url(#nav-lens) blur(1px) saturate(150%);
}

.nav::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  padding: 1px;
  pointer-events: none;
  /* Subtler rim: the same cursor-led gradient, dialed down so the edge
     reads as a faint line of light rather than a drawn border. */
  background: linear-gradient(
    calc(135deg + var(--mx) * 45deg),
    rgba(255, 255, 255, 0) 0%,
    rgba(127, 227, 234, calc(0.16 + var(--mabs) * 0.18)) 20%,
    rgba(255, 255, 255, calc(0.45 + var(--mabs) * 0.15)) 42%,
    rgba(255, 255, 255, 0.07) 60%,
    rgba(230, 238, 238, calc(0.1 + var(--mabs) * 0.12)) 78%,
    rgba(255, 255, 255, 0) 94%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.nav::before, .nav::after, .nav__lens { border-radius: inherit; }

.nav--hidden {
  /* Centering lives on transform (left: 50%), so the hidden state has to
     compose with it rather than replace it. */
  transform: translateX(-50%) translateY(-140%);
  opacity: 0;
  pointer-events: none;
}

.nav__mark { position: relative; z-index: 1; display: block; line-height: 0; }
.nav__mark img {
  display: block;
  width: clamp(84px, 7vw, 100px);
  height: auto;
  /* Stands in for the darkened backdrop the panel used to lean on: a soft
     shadow tucked under the wordmark so it stays legible now that the
     glass itself is actually letting light through. */
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55)) drop-shadow(0 1px 10px rgba(0, 0, 0, 0.3));
}

.nav__links {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--s6);
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--font-text);
  font-size: var(--t-small);
}

.nav__link {
  position: relative;
  display: inline-block;
  padding-block: var(--s1);
  color: var(--ink-soft);
  text-decoration: none;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 1px 8px rgba(0, 0, 0, 0.28);
  transition: color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease-out);
}
.nav__link[aria-current="page"] {
  color: var(--ink);
  font-weight: 500;
}
.nav__link[aria-current="page"]::after { transform: scaleX(1); }
@media (hover: hover) and (pointer: fine) {
  .nav__link:hover { color: var(--ink); }
  .nav__link:hover::after { transform: scaleX(1); }
}
.nav__link:active { transform: translateY(1px); }
/* Hit area, not padding: padding would drag the underline off the word. */
.nav__link::before {
  content: "";
  position: absolute;
  inset: -0.75rem -0.5rem;
}

@media (max-width: 560px) {
  /* A shorter, narrower capsule with its padding still clear of the round
     ends, and a cheaper blur: a phone GPU pays for every pixel of backdrop
     it has to resample, and the panel is on screen for the whole visit. */
  .nav {
    width: calc(100% - var(--gutter) * 2);
    height: 3.25rem;
    gap: var(--s4);
    padding-inline: var(--s5);
  }
  .nav__links { gap: var(--s5); }
  .nav::before {
    backdrop-filter: blur(14px) saturate(155%) brightness(0.62);
    -webkit-backdrop-filter: blur(14px) saturate(155%) brightness(0.62);
  }
}

/* A corner of density under the copy, not a full-frame overlay: it puts
   the contrast where the text is and leaves the rest of the field alone.
   A sibling, never a child of the copy, so the verification pass can
   photograph the frame underneath it. */
.overture__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(120% 80% at 8% 100%,
              rgba(10, 23, 25, 0.72) 0%,
              rgba(10, 23, 25, 0.42) 38%,
              rgba(10, 23, 25, 0) 70%);
}

/* ═══ MOVEMENT II: THE CHAPTERS ═════════════════════════════════════════ */

main { position: relative; z-index: var(--z-copy); }

/* An editorial spread: the argument holds the inner column and the quote
   breaks out into the outer margin, the way it would on a printed page.
   Both columns carry real content, so this is not the banned pattern of a
   giant headline with a small explainer floating beside it. */
.spread { display: grid; gap: var(--s7); margin-top: var(--gap-md); }

@media (min-width: 900px) {
  .spread {
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    gap: var(--s9);
    align-items: start;
  }
  .spread__margin { padding-top: var(--s1); }
}

/* A constrained block of prose sitting under a wider composition. */
.measure-block { max-width: var(--measure); margin-top: var(--gap-md); }
.body--soft    { color: var(--ink-soft); }
.cite--wide    { margin-top: var(--s7); max-width: 74ch; }

/* ── Ch 2. Split stage ──────────────────────────────────────────────────
   Two columns held in tension. The resolve is carried by contrast: the
   past recedes into ink-soft, the present holds full ink and the accent.
   No pinning, because the document has begun. */

.versus { display: grid; gap: var(--s7); margin-top: var(--gap-md); }

@media (min-width: 820px) {
  .versus { grid-template-columns: 1fr 1fr; gap: 0; }
  .versus__side { padding-inline: var(--s7); }
  .versus__side:first-child {
    padding-left: 0;
    border-right: 1px solid var(--hairline);
  }
  .versus__side:last-child { padding-right: 0; }
}

/* Still recedes against the infrastructure side, but readable: ink-soft
   alone read as washed out. Roughly #C8D4D5 on the clear ground. */
.versus__side--past { color: color-mix(in srgb, var(--ink) 70%, var(--ink-soft)); }
.versus__side--now .label { color: var(--accent); }

.versus__list { list-style: none; margin: var(--s5) 0 0; padding: 0; }
.versus__list li { font-size: var(--t-lead); line-height: 1.45; }
.versus__list li + li { margin-top: var(--s4); }

/* ── Ch 3. Documented claim ─────────────────────────────────────────────
   Not a stat row. Each figure carries its own source on the page, which is
   the entire reason this chapter is allowed to use numbers at all. */

.figures { display: grid; gap: var(--gap-md); margin-top: var(--gap-lg); }

@media (min-width: 820px) {
  .figures { grid-template-columns: 1fr 1fr; gap: var(--s9); }
}

.figure { border-top: 1px solid var(--hairline); padding-top: var(--s5); }

.figure__value {
  font-family: var(--font-display);
  font-size: clamp(3.25rem, 7vw, 5rem);
  font-weight: 400;
  line-height: 1;
  letter-spacing: -0.025em;
  font-variation-settings: "opsz" 72;
  margin: 0;
}

.figure__claim {
  font-size: var(--t-lead);
  line-height: 1.45;
  max-width: 38ch;
  text-wrap: pretty;
  margin: var(--s4) 0 0;
}

.figure .cite { margin-top: var(--s5); }

/* ── Ch 4. Annotated diagram ────────────────────────────────────────────
   The peak. Each layer lists the artifacts the reader watched settle into
   it, which is what turns the overture from decoration into an argument. */

.strata-list { list-style: none; margin: var(--gap-lg) 0 0; padding: 0; }

.stratum { display: grid; gap: var(--s4); padding-block: var(--s6); }
.stratum + .stratum { border-top: 1px solid var(--hairline); }

@media (min-width: 900px) {
  .stratum {
    grid-template-columns: minmax(0, 3fr) minmax(0, 8fr);
    gap: var(--s8);
    align-items: start;
  }
}

.stratum__name {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.4vw, 1.875rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: -0.01em;
  font-variation-settings: "opsz" 36;
  margin: 0;
}

.stratum__body > * + * { margin-top: var(--s4); }

/* Set exactly as the artifacts were in the overture, because they are the
   same artifacts. Ink-soft, not the accent: the accent was the edges. */
.stratum__holds {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s5);
  list-style: none;
  padding: 0;
  margin: var(--s5) 0 0;
  font-family: var(--font-mono);
  font-size: var(--t-cite);
  letter-spacing: 0.04em;
  line-height: 1.6;
  color: var(--ink-soft);
}
.stratum__body > .stratum__holds { margin-top: var(--s5); }

/* ── Ch 5. The marquee ──────────────────────────────────────────────────
   Rebuilt four times 2026-09-27. First: the carousel took over the whole
   section from a five-row list, with real logos in place of icon-and-word
   badges. Second, same day: Nekunj pointed at v1's two-row "works with the
   tools you already use" band and asked for that layout instead of one
   row, at a bigger size, with real app-icon marks rather than text. Third,
   same day: moved off paper onto the clear ground, full-bleed, blurred
   across the whole chapter. Fourth, same evening: that whole-chapter blur
   read as a hard seam against #cascades above and left the closing pull
   quote sitting oddly on empty dark field, so at Nekunj's request the
   section went back to white/paper (.stack-panel below) EXCEPT the band
   itself, which stays a "window" onto the blurred live substrate
   (.stack-band::before). See the HTML comment above the section in
   index.html for the full account; report back before changing this
   again. Two independent .stack-marquee rows, each its own <ul> of real
   <li> logos (nothing shadows a hidden <dl> any more, so neither row is
   aria-hidden; the second, visually duplicated pass within each row is,
   so a screen reader hears each row's brands once).

   scroll.js writes each row's .stack-marquee__track translateX off how
   far the section has scrolled through the viewport, so both rows move
   because the reader is scrolling, not on an independent clock, and
   [data-direction="reverse"] on the second row flips its sign so the two
   rows travel opposite ways, the weave effect from the reference. There
   is no pin: the previous pin existed only to hold a tile in sync with a
   matching list row, and there is no row left to sync to. At full bleed
   scroll.js also clones the real pass until each track is at least twice
   its row's width, so a wide viewport never runs the row out of track. */

/* flow-root, not just a plain block: .stack-note and .pull inside here
   carry their own top margin, and without a new block-formatting context
   that margin collapses straight through this div and .shell, landing
   as an unblurred gap between .stack-band and this panel's own white
   background instead of as space inside the white panel. */
.stack-panel { display: flow-root; }

/* Padding, not margin: this space needs to stay inside the band's own
   box so .stack-band::before's blur still covers it. A margin here would
   sit between the band and the white .stack-panel on either side,
   showing a sliver of the raw, unblurred canvas, which is exactly the
   seam this rebuild exists to avoid. */
.stack-band {
  position: relative;
  padding-block: var(--s8);
}

/* The window: this is the one part of the section left transparent, so
   the fixed .substrate canvas shows through, blurred. No fade mask: the
   section is hard-cut white on both sides now, so a uniform blur reads
   as a clean window rather than needing to blend into a neighbour. */
.stack-band::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-backdrop-filter: blur(48px);
  backdrop-filter: blur(48px);
}

/* Positioned descendants with z-index:auto paint above static in-flow
   content in the same stacking context regardless of source order, so
   without this the blur pseudo above would sit on top of the logos
   instead of behind them. */
.stack-band > .stack-marquee { position: relative; z-index: 1; }

.stack-marquee {
  overflow: hidden;
  padding-block: var(--s3);
  mask-image: linear-gradient(to right, transparent, #000 8vw, #000 calc(100% - 8vw), transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8vw, #000 calc(100% - 8vw), transparent);
}

.stack-marquee + .stack-marquee { margin-top: var(--s5); }

.stack-marquee__track {
  display: flex;
  align-items: center;
  gap: var(--s6);
  width: max-content;
  will-change: transform;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A circle, not a pill: with every chip now holding a single app-icon
   mark rather than text of varying width, a fixed-diameter circle reads
   as one consistent grid of icons, the "bigger, more prominent" size
   Nekunj asked for on the second pass (up from a 3.25rem pill), bigger
   again on the full-bleed pass. White against the blurred dark ground,
   the way v1's band read, with a layered, brand-tinted shadow instead of
   a flat one. */
.stack-marquee__chip {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 5.5rem;
  height: 5.5rem;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid rgba(255, 255, 255, 0.5);
  box-shadow: 0 1px 2px rgba(0, 40, 44, 0.22), 0 10px 28px -10px rgba(0, 167, 179, 0.35);
  color: var(--ink-soft);
}

/* Every logo here is a placeholder <img> (see PROGRESS.md for the
   sourcing list); none are hand-drawn from memory, since a subtly wrong
   brand mark is worse than an honest gap. object-fit keeps whatever
   aspect ratio the real file arrives in rather than stretching it to
   fill the circle. Sized to leave a visible ring of the chip showing
   around every icon, so a square app-icon PNG never looks like it is
   about to spill past the badge's own edge. */
.stack-marquee__logo {
  height: 2.75rem;
  width: 2.75rem;
  object-fit: contain;
}

@media (max-width: 540px) {
  .stack-marquee__chip { width: 3.5rem; height: 3.5rem; }
  .stack-marquee__logo { height: 1.75rem; width: 1.75rem; }
  .stack-marquee__track { gap: var(--s4); }
}

/* No JS, or reduced motion: scroll.js never runs, so the track never
   moves. A still band of logos is a complete resting state on its own,
   not a broken animated one. */

.stack-note {
  margin-top: var(--s7);
  max-width: 58ch;
  color: var(--ink-soft);
}

/* ── Ch 6. Sequence ─────────────────────────────────────────────────────
   The one numbered thing on the page, because it is the one thing that is
   genuinely a sequence. */

.steps { list-style: none; counter-reset: step; margin: var(--gap-lg) 0 0; padding: 0; }

.step { counter-increment: step; padding-block: var(--s6); }
.step + .step { border-top: 1px solid var(--hairline); }

.step::before {
  content: counter(step);
  display: block;
  font-family: var(--font-mono);
  font-size: var(--t-cite);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  color: var(--accent);
  margin-bottom: var(--s3);
}

.step__name {
  font-family: var(--font-display);
  font-size: clamp(1.375rem, 2vw, 1.625rem);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.01em;
  font-variation-settings: "opsz" 30;
  margin: 0 0 var(--s3);
}

@media (min-width: 900px) {
  .step {
    display: grid;
    grid-template-columns: 2.5ch minmax(0, 5fr) minmax(0, 7fr);
    column-gap: var(--s5) ;
    align-items: start;
  }
  .step::before  { grid-column: 1; grid-row: 1; margin-bottom: 0; line-height: 1.75; }
  .step__name    { grid-column: 2; grid-row: 1; margin: 0; }
  .step .body    { grid-column: 3; grid-row: 1; }
}

/* ── Ch 7. Colophon ─────────────────────────────────────────────────────
   The ask ends on the same button the overture opens with. "What this is"
   is the last thing read on the page, so it runs at body size rather than
   the small print the rest of the colophon uses. */

.colophon { display: grid; gap: var(--gap-lg); }

.colophon__lede { font-size: var(--t-lead); line-height: 1.5; max-width: 60ch; margin-top: var(--s5); }
.colophon__cta  { margin: var(--s7) 0 0; }

.colophon__meta { display: grid; gap: var(--s7); }
@media (min-width: 820px) {
  .colophon__meta { grid-template-columns: 1fr 1fr; gap: var(--s9); }
}
.colophon__block .body {
  font-size: var(--t-small);
  line-height: 1.55;
  max-width: 46ch;
  margin-top: var(--s3);
}
.colophon__block--close .body {
  font-size: var(--t-body);
  line-height: 1.6;
  max-width: 58ch;
}

/* The sign-off. Padding above the mark is at least its own height, which
   is the brand's clear space rule. The mark is 662 x 117 with the q's
   descender taking the bottom 15% of it, so the place name is lifted by
   that much to sit on the wordmark's baseline rather than under its tail. */
.colophon__foot {
  --mark: clamp(180px, 22vw, 300px);
  border-top: 1px solid var(--hairline);
  padding-top: var(--gap-md);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s5) var(--s7);
}
.colophon__mark {
  display: block;
  width: var(--mark);
  height: auto;
}
.colophon__foot .cite { margin: 0 0 calc(var(--mark) * 0.0265 - 2px); }

/* Reduced motion: the overture stops being three states and becomes one.
   The section collapses to a single screen, the field renders already
   settled, and the copy keeps its place. Fewer and gentler, not zero:
   the reader still gets the headline, the ask and the named layers. */
@media (prefers-reduced-motion: reduce) {
  .overture { height: auto; min-height: 100dvh; }
  .overture__stage { position: relative; height: 100dvh; }
  .overture__copy { transform: none !important; opacity: 1 !important; }
}

/* ═══════════════════════════════════════════════════════════════════════
   THE SUBSTRATE AND THE CLEAR GROUND

   Added in the scroll rebuild. The page gains a WebGL field underneath
   everything, and a third kind of ground: one that does not paint, so the
   field reads through it.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* Chamber scale. One step above anything the document uses, reserved for
     the single place on the page that runs type edge to edge. */
  --t-colossal: clamp(3.5rem, 13vw, 15rem);

  /* The wash over a clear ground. Not decoration: it is the contrast
     guarantee. At the peak the substrate reaches its brightest band and
     light text still measures 6.2:1 through this value. Lower it and that
     number goes with it. */
  --clear-wash: rgba(6, 20, 23, 0.66);
}

.substrate {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  background: var(--void);
}

/* Reduced motion, or no WebGL at all. Same three colour stops, held still.
   The page is never left without its ground. */
.no-substrate .substrate {
  background:
    radial-gradient(70% 50% at 50% 18%, rgba(0, 103, 111, 0.55), transparent 72%),
    radial-gradient(90% 60% at 50% 88%, rgba(0, 167, 179, 0.20), transparent 78%),
    var(--void);
}

/* A ground that does not paint. Inherits the dark ink ramp, then lays a
   wash so the field behind it can never take the text below 4.5:1. */
.ground-clear {
  --ink: #E6EEEE; --ink-soft: #93A6A8;
  --accent: var(--accent-void);
  --hairline: rgba(230, 238, 238, 0.14);
  color: var(--ink);
  background: var(--clear-wash);
}

/* The overture lets more of the field through than any chapter does: its
   own scrim already carries the copy, so the wash can be light. The about
   page's own open screen (.about-open) is built the same way, for the same
   reason, so it shares the value rather than repeating it. */
.overture.ground-void,
.about-open.ground-void { background: rgba(10, 23, 25, 0.28); }

.overture,
.about-open,
main { position: relative; z-index: var(--z-copy); }

/* ── The about page's open screen ───────────────────────────────────────
   Not pinned and carries none of overture.js's settling: just the live
   field showing through, with copy anchored low-left the same way the
   overture's own copy is (.overture__copy and .overture__scrim are reused
   verbatim below, in about.html's markup). */
.about-open {
  position: relative;
  height: 100dvh;
  overflow: hidden;
  isolation: isolate;
}

.byline {
  margin-top: var(--s4);
  font-family: var(--font-text);
  font-size: var(--t-small);
  color: var(--ink-soft);
}

/* ── The letter ────────────────────────────────────────────────────────
   Wherever Nekunj is speaking in his own voice (about.html's start, gap
   and why sections). Newsreader at reading size rather than display size,
   with more air than .body: a letter is read slower than a web page.
   scroll.js inks each word in as the paragraph crosses the reading zone,
   so the voice itself signals "this one is personal" without a label
   saying so. Indented on desktop so the section headings (unindented, at
   the shell's own edge) hang to its left, the way a printed essay sets a
   pull-in paragraph. */
.letter { max-width: 62ch; }
.letter p {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.25rem, 1.9vw, 1.625rem);
  line-height: 1.6;
  letter-spacing: -0.005em;
  text-wrap: pretty;
  margin: 0;
}
.letter p + p { margin-top: var(--s6); }
@media (min-width: 900px) {
  .letter { margin-inline-start: clamp(2rem, 9vw, 8rem); }
}

/* A letter needs more air around it than the generic .chapter-head + *
   rule gives everything else (var(--s5)): declared after that rule, so it
   wins on equal specificity the same way .versus and .figures already
   override it with their own margin-top. */
.chapter-head + .letter,
.letter + .chapter-head,
.letter + .pull { margin-top: var(--s7); }

/* Each word of a .letter paragraph is split into a .rw span and faded in by
   scroll.js's readAlong(), which writes its opacity directly and needs no
   CSS state of its own here. Under no-motion or reduced motion readAlong()
   never runs at all, so the paragraph simply renders as plain text,
   already at full ink. */

/* The chamber's copy, when it carries one (about.html's "where this is heading" section).
   The homepage's own chamber never has a second element under .colossal;
   this is the one place that spacing is needed. */
.chamber__copy { margin-top: var(--gap-md); max-width: var(--measure); }

/* The sign-off under about.html's letter close. Same voice split as the
   rest of the site: serif for the name, tracked mono for the role. */
.signoff {
  margin-top: var(--gap-md);
  padding-top: var(--s6);
  border-top: 1px solid var(--hairline);
  display: grid;
  gap: var(--s2);
}
.signoff__name {
  font-family: var(--font-display);
  font-size: var(--t-quote);
  font-weight: 500;
  letter-spacing: -0.01em;
  font-variation-settings: "opsz" 36;
  margin: 0;
}
.signoff__role {
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
}

/* The about page's close. Same veil as the homepage's (.chapter--close.
   ground-void), but not that rule itself: that one is also selected by
   ".chapter--collapse, .chapter--peak, .chapter--close { padding-block: 0 }"
   because all three hold a pinned .stage. This section holds no pin and
   needs its ordinary .band padding, so it gets its own class with the same
   gradient values instead of inheriting the pin-coupled one. */
.about-close.ground-void {
  --accent: var(--accent-teal);
  background: linear-gradient(
    180deg,
    rgba(10, 23, 25, 0.66) 0%,
    rgba(10, 23, 25, 0.88) 46%,
    var(--void) 100%
  );
}

/* ── Media slots ────────────────────────────────────────────────────────
   One treatment, applied identically whether the slot holds generated
   footage, stock footage, or nothing at all. That is what makes three
   different sources read as one authored system.

   Empty, the gradient is the light source. Filled, it is the duotone bed
   the footage is graded onto. Neither case needs a CSS change. */

.media {
  position: absolute;
  inset: -12% 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  background:
    radial-gradient(58% 44% at 22% 64%, rgba(0, 167, 179, 0.32), transparent 70%),
    radial-gradient(46% 36% at 78% 26%, rgba(127, 227, 234, 0.15), transparent 72%);
}

.media video,
.media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: contrast(1.12) saturate(0.55) brightness(0.70);
  mix-blend-mode: luminosity;
}

/* ── The word mask ──────────────────────────────────────────────────────
   Every heading assembles out of its own baseline. The padding and the
   matching negative margin exist so descenders are not clipped by the
   mask, which is the one detail that makes this trick look broken. */

/* Held until scroll.js has written each reveal's starting state, so the
   first paint cannot show text that is about to be hidden and replayed.
   The class is set by an inline script in the head and removed by
   scroll.js, or by a timeout there if scroll.js never runs. Reduced motion
   is excluded outright: those readers get the document as it stands. */
@media (prefers-reduced-motion: no-preference) {
  .reveal-wait [data-words],
  .reveal-wait [data-reveal],
  .reveal-wait [data-read],
  .reveal-wait .step { opacity: 0; }
}

.w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.wi { display: inline-block; }

/* ── The chamber ────────────────────────────────────────────────────────
   The seam. The only place on the page type runs at chamber scale, and the
   only section that uses the full width edge to edge rather than the
   shell. It carries no argument, which is why it can afford to be loud. */

.chamber {
  position: relative;
  min-height: 100dvh;
  display: grid;
  align-items: center;
  overflow: hidden;
  isolation: isolate;
  background: rgba(6, 20, 23, 0.50);
  --ink: #F2F8F8; --ink-soft: #8FA6A8;
  --accent: var(--accent-void);
  color: var(--ink);
}

.chamber__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: var(--s9) var(--gutter);
}

.colossal {
  font-family: var(--font-text);
  font-size: var(--t-colossal);
  font-weight: 600;
  line-height: 0.88;
  letter-spacing: -0.035em;
  text-wrap: balance;
  margin: 0;
  max-width: 14ch;
}

/* Technical marginalia at the frame edge, the way the reference sites
   annotate their own hero. Real strings from the lean framework the
   chapter beneath cites, never invented chrome. */
.chamber__edge {
  position: absolute;
  top: 0;
  white-space: nowrap;
}
.chamber__edge--l { left: var(--gutter); }
.chamber__edge--r { right: var(--gutter); text-align: right; }

@media (max-width: 700px) {
  .chamber__edge--r { display: none; }
  .colossal { max-width: none; }
}

/* ── Ch 3. The collapse ─────────────────────────────────────────────────
   Pinned. The figures climb rather than sit, and the four causes advance
   one at a time. Opacity and transform are written by scroll.js; the only
   job here is to keep the frame from reflowing while they do. */

/* No will-change, transform, filter or perspective on a chapter that
   contains a pinned stage. Any of them makes the chapter a containing
   block for fixed-position descendants, ScrollTrigger pins by setting
   position: fixed, and the pin then scrolls away with the page instead of
   holding. The symptom is an entirely blank viewport where the peak
   should be, which reads as a rendering failure rather than a CSS one. */

.causes { margin-top: var(--gap-lg); }
.causes__title { color: var(--accent); }

.causes__list {
  list-style: none;
  counter-reset: cause;
  margin: var(--s5) 0 0;
  padding: 0;
  max-width: 54ch;
}

.cause {
  counter-increment: cause;
  position: relative;
  padding-left: 3.25ch;
  font-size: var(--t-lead);
  line-height: 1.45;
}
.cause + .cause { margin-top: var(--s4); }

.cause::before {
  content: counter(cause);
  position: absolute;
  left: 0;
  top: 0.35em;
  font-family: var(--font-mono);
  font-size: var(--t-cite);
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

/* ── Ch 4. The peak ─────────────────────────────────────────────────────
   Pinned. The strata list is translated by scroll.js so the active layer
   holds the optical centre; the meter is the only wayfinding on a page
   that otherwise refuses chrome, and it earns its place because this is
   the one section where the reader cannot see how much is left. */

.peak__meter {
  height: 1px;
  background: var(--hairline);
  margin-top: var(--s7);
  overflow: hidden;
}
.peak__meter i {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
}

.chapter--peak .strata-list { margin-top: var(--s7); }
.stratum { transition: none; }
.stratum.is-active .stratum__name { color: var(--accent); }

/* ── Ch 6. The sequence ─────────────────────────────────────────────────
   A rule that draws itself as the reader travels. The one linear treatment
   on the page, spent on the one thing that is genuinely linear. */

.steps-wrap { position: relative; padding-left: var(--s5); }

.steps__rule {
  position: absolute;
  left: 0;
  top: var(--gap-lg);
  bottom: var(--s6);
  width: 1px;
  background: var(--hairline);
}
.steps__rule i {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: top center;
}

@media (min-width: 900px) {
  .steps-wrap { padding-left: var(--s6); }
}

/* ── Ch 7. The close ────────────────────────────────────────────────────
   Pinned. Peak-end: the ask holds alone, and the supporting material
   arrives underneath it only once the ask has been read. */

.colophon__rest { display: grid; gap: var(--gap-lg); }

/* Same device as the overture: a veil over the field rather than a ground
   painted on top of it, so the substrate reads through. It deepens down the
   page and lands on solid void at the foot, which gives the last screen an
   actual bottom instead of trailing off. The gradient is also the seam with
   the chapter above, since that one is clear and this one is not. */
/* The brighter of the three accent stops, for the same reason chapter 4
   uses it: this ground is not a painted colour, it is a live field seen
   through a veil, and the veil is thinnest exactly where the one link
   sits. Measured against the bare ground behind it, #00A7B3 bottomed out
   at 3.62:1 on 22px regular, which needs 4.5. */
.chapter--close.ground-void {
  --accent: var(--accent-teal);
  background: linear-gradient(
    180deg,
    rgba(10, 23, 25, 0.66) 0%,
    rgba(10, 23, 25, 0.88) 46%,
    var(--void) 100%
  );
}

/* ── No motion ──────────────────────────────────────────────────────────
   GSAP failed to load, or the reader asked for stillness. The only thing
   CSS ever hides is the first-paint hold, and that is lifted on this path
   too, so there is little to undo: this only guarantees that the three
   scroll-written transforms cannot leave anything mid-flight. */

.no-motion .strata-list,
.no-motion .colophon__ask,
.no-motion .colophon__rest,
.no-motion .cause,
.no-motion .stratum {
  transform: none !important;
  opacity: 1 !important;
}
.no-motion .peak__meter i { transform: scaleX(1); }
.no-motion .steps__rule i { transform: scaleY(1); }

@media (prefers-reduced-motion: reduce) {
  .media { display: none; }
}

/* ── The pinned stage ───────────────────────────────────────────────────
   A pin only works if what it holds fits the viewport. The first build of
   this page pinned whole chapters: the figures at the collapse scrolled
   off the top before they could finish counting, and the strata list at
   the peak translated straight up into its own heading. A pinned chapter
   now holds a stage that is exactly one screen, and everything it cannot
   show for the whole hold lives in .after and scrolls normally once the
   pin releases. */

.chapter--collapse,
.chapter--peak,
.chapter--close { padding-block: 0; }

.stage {
  position: relative;
  height: 100vh;
  display: grid;
  align-content: center;
  overflow: hidden;
}

.after { position: relative; padding-block: var(--section); }

/* The stack chapter never pins, at any width (it did, to hold a ring tile
   in sync with a matching list row; both are gone, replaced 2026-09-27 by
   a plain scrolling logo strip with nothing left to synchronise). Its
   stage behaves like an ordinary block rather than the 100vh pinned frame
   other staged chapters use, same shape every other unpinned or
   reduced-motion stage on the page falls back to. */
.chapter--stack .stage {
  height: auto;
  display: block;
  overflow: visible;
  padding-block: var(--section) 0;
}
.chapter--stack .after { padding-top: var(--s7); }

/* The whole-chapter blur that used to sit here (.chapter--stack::before)
   is gone as of the 2026-09-27 evening white-panel rebuild: the section
   is white/paper again except the band itself, so the blur moved to
   .stack-band::before, scoped to that box alone. See the Ch 5 comment
   block above .stack-panel for why. */

/* Tighter rhythm inside a stage. The page rhythm is written for sections
   that can grow; a stage cannot. */
.stage .figures { margin-top: var(--s7); gap: var(--s6) var(--s9); }
.stage .causes  { margin-top: var(--s7); }
.stage .chapter-head + * { margin-top: var(--s4); }

/* ── The peak stack ─────────────────────────────────────────────────────
   Five layers occupying one place, one at a time. Absolutely positioned
   rather than flowed, because the reader is moving through a stack rather
   than scrolling past a list, and a flowed list cannot say that inside a
   fixed frame.

   The panel is the contrast guarantee. The substrate is at its brightest
   here, and its bands run straight through the text column. */

/* The frame height is written by scroll.js from the tallest layer plus this
   padding, so the panel hugs its contents. min-height is only the pre-script
   floor: a fixed 30vh both left dead panel under the shorter layers and, on
   a short viewport, cut the tallest one off at the bottom edge. */
.chapter--peak .strata-list {
  position: relative;
  margin-top: var(--s6);
  min-height: 30vh;
  /* Clips a stratum once it has travelled clear of the box, which is how
     scroll.js keeps two crossfading layers of real text from ever being
     readable at the same time. The panel radius moves here so the clip
     edge and the panel edge are the same curve. */
  overflow: hidden;
  border-radius: var(--r-3);
}

/* Fills the frame exactly. It used to be inset negatively to bleed past the
   text, but the clip that the frame needs cropped the bleed away, so the
   panel edge landed on the text edge and every layer sat flush against the
   corner. The air now lives as padding on the layer itself, which is the
   only place the clip cannot take it back. */
.chapter--peak .strata-list::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(5, 18, 21, 0.68);
  border: 1px solid rgba(127, 227, 234, 0.10);
  border-radius: var(--r-3);
}

/* An absolutely positioned child resolves inset: 0 against the padding box
   of its container, so padding on the list would not move it. It has to be
   padding on the layer. */
.chapter--peak .stratum {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 1;
  padding: var(--s5);
  border-top: 0;
}
@media (min-width: 900px) {
  .chapter--peak .stratum { padding: var(--s6) var(--s7); }
}
.chapter--peak .stratum + .stratum { border-top: 0; }

/* Without motion the stack has to become a list again, or all five layers
   render on top of each other and the peak is unreadable. */
.no-motion .stage { height: auto; padding-block: var(--section); }
.no-motion .chapter--peak .strata-list { min-height: 0; }
.no-motion .chapter--peak .strata-list::before { display: none; }
/* No panel without motion, so no panel padding either: the horizontal inset
   would push these layers out of the column every other section sits in. */
.no-motion .chapter--peak .stratum {
  position: static;
  padding: var(--s6) 0;
}
.no-motion .chapter--peak .stratum + .stratum {
  border-top: 1px solid var(--hairline);
}

/* ═══════════════════════════════════════════════════════════════════════
   THE PHONE EDITION

   Same document, same ground, same words, set for a hand instead of a desk.
   A long page on a phone reads well when each screen has one dominant thing,
   when references step aside until asked for, and when the field behind the
   type stays quiet under a reading line. Nothing here changes a
   desktop pixel: every rule below is width-scoped, and the tokens it
   remaps are the ones 5a introduced with desktop-equal defaults.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Folded sources: structure at every width ─────────────────────────── */
.source > .cite { margin: 0; }
.flow > * + .source { margin-top: var(--s7); }
.figure .source { margin-top: var(--s5); }
.source--wide { margin-top: var(--s7); max-width: 74ch; }
.source__tag { list-style: none; }
.source__tag::-webkit-details-marker { display: none; }

@media (min-width: 760px) {
  .source__tag { display: none; }
}

/* ── Below 900: the one pin that cannot hold a phone screen ──────────────
   The collapse no longer pins here (scroll.js), so its stage flows like
   the stack chapter's always does now. Both hand straight on to their
   .after with a normal paragraph gap instead of two stacked section
   paddings. */
@media (max-width: 899.98px) {
  .chapter--collapse .stage {
    height: auto;
    display: block;
    overflow: visible;
    padding-block: var(--section) 0;
  }
  .chapter--collapse .after { padding-top: var(--s7); }

  /* The method's numbers become the spine's landmarks: the rule draws down
     the left, and each step opens on a numeral you can find with a thumb. */
  .step::before {
    font-family: var(--font-display);
    font-size: 1.5rem;
    line-height: 1;
    letter-spacing: 0;
    font-variation-settings: "opsz" 36;
    margin-bottom: var(--s3);
  }
}

/* ── Below 820: the comparison is a table ─────────────────────────────────
   Same two columns as desktop, compact. Subgrid shares the row tracks
   between the two lists, so each automation line sits level with the
   infrastructure line it answers, without changing the markup. The row
   count (1 label + 4 items) is written in: if a list ever gains an item,
   change repeat(5, auto). */
@media (max-width: 819.98px) {
  .versus {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: repeat(5, auto);
    gap: 0;
  }
  .versus__side {
    display: grid;
    grid-row: 1 / -1;
    grid-template-rows: subgrid;
  }
  .versus__side--past {
    grid-column: 1;
    padding-right: var(--s4);
    border-right: 1px solid var(--hairline);
  }
  .versus__side--now {
    grid-column: 2;
    padding-left: var(--s4);
  }
  .versus .label { padding-bottom: var(--s3); }
  .versus__list {
    display: grid;
    grid-row: 2 / -1;
    grid-template-rows: subgrid;
    margin: 0;
  }
  .versus__list li {
    font-size: 0.9375rem;
    line-height: 1.4;
    padding-block: var(--s3);
    border-top: 1px solid var(--hairline);
  }
  .versus__list li + li { margin-top: 0; }
}

/* ── Below 760: the phone itself ──────────────────────────────────────── */
@media (max-width: 759.98px) {
  :root {
    --gap-lg: var(--s7);
    --gap-md: var(--s6);
    --t-body: 1.0625rem;
    --t-quote: 1.375rem;
  }

  .body { line-height: 1.6; }
  .pull { line-height: 1.3; }
  .cite { letter-spacing: 0.04em; line-height: 1.6; }

  /* Calm under the reading line. Same field, a slightly deeper veil. The
     peak keeps 0.66 because the bands are what it is showing; the homepage
     chamber keeps its own 0.50 because it holds one line of display type,
     not reading text. The about page's chamber carries paragraphs, so it
     gets the reading veil. */
  .ground-clear:not(.chapter--peak):not(.chamber) { background: rgba(6, 20, 23, 0.76); }
  .chamber:has(.chamber__copy) { background: rgba(6, 20, 23, 0.76); }

  /* The chamber was a full screen of mostly empty dark straight after the
     overture's own settled hold. Most of a screen still reads as a moment. */
  .chamber { min-height: 80svh; }
  .chamber__inner { padding-block: var(--s7); }

  /* The settling's labels, one step larger now there are only ten. */
  .overture__nodes li { font-size: 0.75rem; }

  /* Pins size to the small viewport, so the browser's own toolbars cannot
     cover the bottom of a held frame. */
  .stage { height: 100vh; height: 100svh; }

  /* The peak has to fit one phone screen with its heading. */
  .chapter--peak .peak__meter { margin-top: var(--s5); }
  .chapter--peak .strata-list { margin-top: var(--s4); }
  .chapter--peak .stratum { padding: var(--s4); }
  .chapter--peak .stratum__body > * + * { margin-top: var(--s3); }
  .chapter--peak .stratum__body > .stratum__holds { margin-top: var(--s4); }
  .chapter--peak .body--soft { font-size: var(--t-small); line-height: 1.5; }

  /* Folded sources: a chip that names its source and opens on a tap. */
  .source__tag {
    display: inline-flex;
    align-items: center;
    gap: var(--s3);
    min-height: 2.5rem;
    padding-inline: var(--s4) var(--s3);
    font-family: var(--font-mono);
    font-size: var(--t-cite);
    letter-spacing: 0.04em;
    color: var(--ink-soft);
    border: 1px solid var(--hairline);
    border-radius: var(--r-2);
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    transition: transform var(--dur-fast) var(--ease-out);
  }
  .source__tag::after {
    content: "+";
    font-size: 0.875rem;
    line-height: 1;
    color: var(--accent);
    transition: transform var(--dur) var(--ease-out);
  }
  .source[open] > .source__tag::after { transform: rotate(45deg); }
  .source__tag:active { transform: translateY(1px); }
  .source[open] > .cite,
  .source[open] > .sources { margin-top: var(--s3); }
  .colophon__sources > .source { margin-top: var(--s4); }
}

@media (max-width: 759.98px) and (hover: hover) and (pointer: fine) {
  .source__tag:hover { color: var(--ink); border-color: var(--accent); }
}

/* A short phone cannot hold a peak layer and its artifact list together.
   The list is the one thing the layer can lose without losing its meaning,
   since the same names were on screen in the overture. */
@media (max-width: 759.98px) and (max-height: 700px) {
  .chapter--peak .stratum__holds { display: none; }
}

/* A short phone also cannot hold both the technical line and the plain
   example. The example is the one a reader without a technical vocabulary
   needs, so it stays and the technical line yields, on phones only. */
@media (max-width: 759.98px) {
  .chapter--peak .stratum .body--soft { display: none; }
}

/* Chapter 4: plain example per layer */
.stratum__eg-tag {
  display: block;
  margin-bottom: var(--s1);
  font: 500 var(--t-label)/1.2 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Cascades: one layer, under every department (after #architecture) */

.chapter--cascades .cx { margin-top: var(--s9); }

/* The rays stay, out of focus: this chapter is for reading. The mask ramps
   the blur in over the top edge so there is no seam against the peak. */
.chapter--cascades::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-backdrop-filter: blur(48px);
  backdrop-filter: blur(48px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 min(28vh, 260px), #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 min(28vh, 260px), #000 100%);
}
.chapter--cascades > .shell { position: relative; z-index: 1; }

/* tabs: type and an underline, no boxes */
.cx-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s6);
  border-bottom: 1px solid var(--hairline);
}
.cx-tab {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: var(--s2);
  padding: var(--s3) 0 var(--s4);
  font: 400 var(--t-body)/1.3 var(--font-text);
  color: var(--ink-soft);
  text-align: left;
  background: none;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  transition: transform var(--dur-fast) var(--ease-out);
}
.cx-tab::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--accent);
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  transition:
    opacity var(--dur) var(--ease-out),
    transform 420ms var(--ease-out);
}
.cx-tab:hover { color: var(--ink); }
.cx-tab:hover::after { opacity: 0.4; transform: scaleX(1); }
.cx-tab:active { transform: translateY(1px); }
.cx-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: var(--r-1); }
.cx-tab[aria-selected="true"] { color: var(--ink); }
.cx-tab[aria-selected="true"]::after { opacity: 1; transform: scaleX(1); }
.cx-tab__n {
  font: 400 var(--t-small)/1 var(--font-text);
  font-variant-numeric: tabular-nums;
  color: var(--ink-soft);
}

/* the line: six departments standing on one layer */
.cx-line { margin: var(--s8) 0 0; }
.cx-line__depts {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
}
.cx-node {
  position: relative;
  padding: 0 var(--s3) var(--s6) 0;
  border-bottom: 1px solid var(--hairline);
  font: 400 var(--t-lead)/1.2 var(--font-display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.cx-node > span { display: inline-block; }
/* the tick down to the layer */
.cx-node::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 1px;
  height: calc(var(--s6) - var(--s2));
  background: var(--accent);
  opacity: 0;
  transform: scaleY(0);
  transform-origin: center bottom;
}
/* where it meets the layer */
.cx-node::after {
  content: "";
  position: absolute;
  left: -3px;
  bottom: -4px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
  transform: scale(0);
}
.cx-line__cap {
  margin-top: var(--s3);
  font: italic 400 var(--t-small)/1.4 var(--font-display);
  color: var(--ink-soft);
}

/* the panels */
.cx-panels { margin-top: var(--s8); }
.cx-trigger {
  margin: 0;
  max-width: 34ch;
  font: 400 var(--t-quote)/1.25 var(--font-display);
  letter-spacing: -0.01em;
  color: var(--ink);
}
.cx-dept {
  display: block;
  font: 400 var(--t-small)/1.4 var(--font-text);
  color: var(--accent);
}
.cx-trigger .cx-dept { margin-bottom: var(--s4); }
.cx-ripples { list-style: none; margin: var(--s7) 0 0; padding: 0; display: grid; gap: var(--s6); }
.cx-ripple {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: 2px var(--s5);
}
.cx-ripple .cx-dept { grid-column: 1; margin: 0; font-weight: 500; line-height: 1.7; color: var(--ink); }
.cx-layer {
  grid-column: 1;
  font: 400 var(--t-small)/1.4 var(--font-text);
  color: var(--ink-soft);
}
.cx-ripple p {
  grid-column: 2;
  grid-row: 1 / span 2;
  margin: 0;
  font: 400 var(--t-body)/1.65 var(--font-text);
  color: var(--ink);
}
.cx-ask {
  margin-top: var(--s7);
  padding-top: var(--s5);
  border-top: 1px solid var(--hairline);
}
.cx-ask__tag {
  display: block;
  margin-bottom: var(--s2);
  font: 400 var(--t-small)/1.4 var(--font-text);
  color: var(--accent);
}
.cx-ask p { margin: 0; font: 400 var(--t-lead)/1.4 var(--font-display); color: var(--ink); }
.cx-unasked {
  margin: var(--s5) 0 0;
  max-width: 48ch;
  font: italic 400 var(--t-body)/1.6 var(--font-display);
  color: var(--ink-soft);
}
.chapter--cascades .cx-close { margin-top: var(--s9); }
.chapter--cascades .cx-note { margin-top: var(--s5); }

@media (min-width: 900px) {
  .cx-panel {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    column-gap: var(--s9);
    align-items: start;
  }
  .cx-trigger { grid-column: 1; grid-row: 1 / span 3; }
  .cx-ripples, .cx-ask, .cx-unasked { grid-column: 2; }
  .cx-ripples { margin-top: 0; }
  .cx-ask { margin-top: var(--s7); }
}
@media (max-width: 759.98px) {
  .chapter--cascades::before,
  .stack-band::before { -webkit-backdrop-filter: blur(28px); backdrop-filter: blur(28px); }
  .cx-tabs { display: grid; grid-template-columns: 1fr 1fr; column-gap: var(--s5); border-bottom: 0; }
  .cx-tab {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s1);
    padding: var(--s3) 0;
    font-size: var(--t-small);
    border-bottom: 1px solid var(--hairline);
  }
  .cx-line__depts { grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: var(--s5); }
  .cx-node { padding-bottom: var(--s5); font-size: var(--t-body); }
  .cx-node::before { height: calc(var(--s5) - var(--s2)); }
}
@media (max-width: 559.98px) {
  .cx-ripple { grid-template-columns: minmax(0, 1fr); }
  .cx-ripple .cx-dept, .cx-layer, .cx-ripple p { grid-column: 1; grid-row: auto; }
  .cx-ripple p { margin-top: var(--s1); }
}

/* JS on: panels share one cell so switching never changes page height */
.cx-ready .cx-panels { display: grid; }
.cx-ready .cx-panel { grid-area: 1 / 1; visibility: hidden; }
.cx-ready .cx-panel.is-active { visibility: visible; }

/* resting state, then the cascade */
.cx-ready .cx-node > span { opacity: 0.5; }
.cx-ready :is(.cx-ripple, .cx-ask, .cx-unasked) { opacity: 0; transform: translateY(10px); }
.cx-ready.is-on .cx-node.is-lit > span { opacity: 1; }
.cx-ready.is-on .cx-node.is-lit::before { opacity: 1; transform: scaleY(1); }
.cx-ready.is-on .cx-node.is-lit::after { opacity: 1; transform: scale(1); }
.cx-ready.is-on .cx-node.is-source::after { transform: scale(1.5); }
.cx-ready.is-on .cx-panel.is-active :is(.cx-ripple, .cx-ask, .cx-unasked) { opacity: 1; transform: none; }
.cx-ready.is-on .cx-node > span,
.cx-ready.is-on .cx-node::before,
.cx-ready.is-on .cx-node::after,
.cx-ready.is-on :is(.cx-ripple, .cx-ask, .cx-unasked) {
  transition:
    opacity 420ms var(--ease-out) var(--d, 0ms),
    transform 520ms var(--ease-out) var(--d, 0ms);
}

.no-motion .cx, .no-motion .cx *, .no-motion .cx *::before, .no-motion .cx *::after { transition: none !important; animation: none !important; }
@media (prefers-reduced-motion: reduce) {
  .cx, .cx *, .cx *::before, .cx *::after { transition: none !important; animation: none !important; }
}

/* ── Frosted reading ground, from "One layer, under every department" to the end
   One wash and one blur per chapter ARTICLE, nothing nested inside. The
   chapters sit edge to edge with no margins, so the frost is continuous:
   no gap, and no second layer stacking a different darkness on a panel,
   the logo strip or the closing quote. The rays stay legible as shapes. */
:root {
  --frost-blur: 30px;
  --frost-wash: rgba(6, 20, 23, 0.8);
}

.chapter--cascades,
#method,
.chapter--stack,
.chapter--close.ground-void {
  position: relative;
  background: var(--frost-wash);
}
.chapter--close.ground-void {
  background: linear-gradient(180deg, rgba(6, 20, 23, 0.8) 0%, rgba(8, 21, 23, 0.9) 46%, var(--void) 100%);
}

/* inside the stack chapter the panels and the strip are just content */
.chapter--stack .stack-panel.ground-clear:not(.x):not(.y),
.chapter--stack .stack-band { background: transparent; }
.chapter--stack .stack-band::before { content: none; }

.chapter--cascades::before,
#method::before,
.chapter--stack::before,
.chapter--close.ground-void::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-backdrop-filter: blur(var(--frost-blur));
  backdrop-filter: blur(var(--frost-blur));
}
/* cascades keeps its ramp so the blur eases in under the peak */
.chapter--cascades::before {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 min(28vh, 260px), #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 min(28vh, 260px), #000 100%);
}
#method > .shell,
.chapter--stack > .stage,
.chapter--stack > .stack-panel,
.chapter--close.ground-void > * { position: relative; z-index: 1; }

@media (max-width: 759.98px) {
  :root { --frost-blur: 20px; }
  .chapter--cascades.ground-clear:not(.chapter--peak),
  .chapter--stack { background: var(--frost-wash); }
}

/* ═══════════════════════════════════════════════════════════════════════
   THE BLUEPRINT PAGE (2026-10-22)
   The homepage rebuilt on Nekunj's Page Blueprint: mostly white, three
   dark statement sections (hero, process, contact) on the chamber's
   ground. Appended last on purpose so it wins the cascade; the older
   blocks above still serve about.html.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Grounds ──────────────────────────────────────────────────────────── */

/* The chamber's ground, made reusable: the substrate at p 0.12 under the
   chamber's own 0.50 wash, with the .media two-blob glow. Deliberately no
   isolation, overflow or transform here: #process pins inside it. */
.ground-chamber {
  --ink: #F2F8F8; --ink-soft: #8FA6A8;
  --accent: var(--accent-void);
  --hairline: rgba(230, 238, 238, 0.14);
  --body-track: 0.004em;
  position: relative;
  color: var(--ink);
  background: rgba(6, 20, 23, 0.50);
}
.ground-chamber .media { inset: 0; }
.ground-chamber .media ~ * { position: relative; z-index: 1; }

/* White, alternating with paper so consecutive light sections read as
   separate without a rule between them. Same ink ramp as paper. */
.ground-white {
  background: var(--surface);
  --ink: #0D1F22; --ink-soft: #4A5A5C;
  --accent: var(--accent-paper);
  --hairline: #DCE3E3;
  --body-track: 0;
  color: var(--ink);
}

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.icon {
  width: 1.25rem;
  height: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.link:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-1); }

button.button { cursor: pointer; -webkit-appearance: none; appearance: none; }
button.button[disabled] { opacity: 0.6; cursor: progress; }

/* A quiet text button: label in accent, an underline that grows on hover. */
.text-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  min-height: 2.75rem;
  margin-top: auto;
  padding: 0;
  font: 600 0.9375rem/1 var(--font-text);
  color: var(--accent);
  background: none;
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-fast) var(--ease-out);
}
.text-btn::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0.55rem;
  height: 1px;
  background: currentColor;
  transform: scaleX(0.35);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .text-btn:hover::after { transform: scaleX(1); }
}
.text-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-1); }
.text-btn:active { transform: translateY(1px); }

/* ── 1. Hero ──────────────────────────────────────────────────────────── */

.hero { min-height: 100svh; display: grid; align-items: center; }
.hero__inner { padding-block: calc(var(--s10) + var(--s4)) var(--s9); }

/* The chamber's type, at a size that holds an eight-word line in about
   three lines on desktop. */
.hero-head {
  font-family: var(--font-text);
  font-weight: 600;
  font-size: clamp(2.75rem, 7vw, 7rem);
  line-height: 0.94;
  letter-spacing: -0.035em;
  text-wrap: balance;
  max-width: 15ch;
  margin: 0;
}
.hero__sub { margin-top: var(--s6); max-width: 46ch; }
.hero__cta { margin: var(--s7) 0 0; }

/* ── 2A. What it is ───────────────────────────────────────────────────── */

.define { display: grid; gap: var(--s6); }
.define__text {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.625rem, 3vw, 2.5rem);
  line-height: 1.22;
  letter-spacing: -0.012em;
  font-variation-settings: "opsz" 48;
  text-wrap: balance;
  max-width: 26ch;
  margin: 0;
}
.define__more { margin-top: var(--s5); }
.define__approve { display: block; margin-top: var(--s3); color: var(--accent); font-weight: 500; }

/* ── 2B. What is inside one ───────────────────────────────────────────── */

.parts {
  position: relative;
  margin-top: var(--s8);
  padding: var(--s7) var(--s5) var(--s6);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  box-shadow: 0 1px 2px rgba(0, 40, 44, 0.04), 0 24px 60px -36px rgba(0, 114, 122, 0.30);
}
.parts__name {
  position: absolute;
  top: 0; left: var(--s5);
  transform: translateY(-50%);
  margin: 0;
  padding: 0 var(--s3);
  background: var(--surface);
  font: 500 var(--t-cite)/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}
.parts__flow,
.parts__frame { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s6); position: relative; }
/* The line work flows along. Vertical through the icons on phones. */
.parts__flow::before {
  content: "";
  position: absolute;
  left: 1.5rem; top: 1.5rem; bottom: 1.5rem;
  width: 1px;
  background: var(--hairline);
}
.parts__frame { margin-top: var(--s7); padding-top: var(--s6); border-top: 1px dashed var(--hairline); }

.part { display: grid; grid-template-columns: 3rem minmax(0, 1fr); column-gap: var(--s4); align-items: start; }
.part > :not(.part__icon) { grid-column: 2; }
.part__icon,
.path__icon {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: var(--accent);
  box-shadow: 0 1px 2px rgba(0, 40, 44, 0.06), 0 8px 20px -10px rgba(0, 114, 122, 0.28);
}
.part__icon { grid-column: 1; grid-row: 1 / span 3; }
.part__n {
  margin: 0 0 var(--s1);
  font: 400 var(--t-cite)/1.4 var(--font-mono);
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}
.part__title {
  margin: 0 0 var(--s2);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.25rem, 1.6vw, 1.5rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.part__line { margin: 0; max-width: 40ch; font-size: var(--t-small); line-height: 1.55; color: var(--ink-soft); }

/* ── 2C. Example cards ────────────────────────────────────────────────── */

.eg-list { list-style: none; margin: var(--s8) 0 0; padding: 0; display: grid; gap: var(--s5); }
.eg {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--s6);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  box-shadow: 0 1px 2px rgba(0, 40, 44, 0.05), 0 18px 40px -22px rgba(0, 114, 122, 0.28);
}
.eg__n {
  margin: 0 0 var(--s3);
  font: 400 var(--t-cite)/1.4 var(--font-mono);
  letter-spacing: 0.08em;
  color: var(--accent);
}
.eg__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.375rem, 2vw, 1.625rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.eg__faces { display: grid; margin: var(--s4) 0 var(--s5); }
.eg__tag {
  margin: 0 0 var(--s2);
  font: 500 var(--t-cite)/1.4 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.eg__text { margin: 0; font-size: var(--t-body); line-height: 1.55; }

/* Two faces in one grid cell: the card never changes height. Shared with
   the process cards. */
.eg__face,
.proc-card__face {
  grid-area: 1 / 1;
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out), visibility 0s linear 0s;
}
.eg__face--there,
.proc-card__more {
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition-delay: 0s, 0s, var(--dur);
}
.is-open .eg__face--here,
.is-open .proc-card__sum {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition-delay: 0s, 0s, var(--dur);
}
.is-open .eg__face--there,
.is-open .proc-card__more {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s, 0s, 0s;
}

.eg-dots { display: flex; justify-content: center; gap: var(--s2); margin-top: var(--s5); }
.eg-dots i {
  width: 0.5rem; height: 0.5rem;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.22;
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.eg-dots i.is-on { opacity: 1; transform: scale(1.15); }

/* Below 900px: a scroll-snap carousel, internal to the list, never the page. */
@media (max-width: 899.98px) {
  .eg-list {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    margin-inline: calc(-1 * var(--gutter));
    padding: var(--s2) var(--gutter) var(--s5);
    scroll-padding-inline: var(--gutter);
  }
  .eg-list::-webkit-scrollbar { display: none; }
  .eg { flex: 0 0 min(85%, 24rem); scroll-snap-align: start; }
}
@media (min-width: 900px) {
  .eg-list { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s6); }
  .eg-dots { display: none; }
}

/* ── 2C, continued. The process walkthrough ───────────────────────────── */

.proc { padding-block: var(--section); }
.proc__note {
  margin: var(--s6) 0 0;
  max-width: 60ch;
  padding-left: var(--s4);
  border-left: 2px solid var(--accent);
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--t-lead);
  line-height: 1.45;
  color: var(--ink);
}
.proc__stage { position: relative; margin-top: var(--s8); }
.proc__bar { margin-bottom: var(--s5); }
.proc__flag {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  margin: 0;
  padding: var(--s2) var(--s3);
  border: 1px solid rgba(127, 227, 234, 0.35);
  border-radius: var(--r-2);
  background: rgba(10, 23, 25, 0.6);
  font: 500 var(--t-label)/1.3 var(--font-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-teal);
}
.proc__flag-dot { width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--accent-teal); }

/* Default layout (phones, no JS, reduced motion): a readable grid. */
.proc__track {
  list-style: none;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: grid;
  gap: var(--s4);
}
@media (min-width: 760px) {
  .proc__track { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.proc-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--s5);
  background: rgba(13, 31, 34, 0.72);
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04), 0 24px 48px -28px rgba(0, 0, 0, 0.7);
  transition: opacity var(--dur) var(--ease-out);
}
/* The lit edge of the active stage, faded in rather than transitioned. */
.proc-card::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  border: 1px solid rgba(0, 167, 179, 0.75);
  box-shadow: 0 0 44px -14px rgba(0, 167, 179, 0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease-out);
}
.proc-card.is-active::after { opacity: 1; }
.proc-card__n {
  margin: 0 0 var(--s3);
  font: 400 var(--t-cite)/1.4 var(--font-mono);
  letter-spacing: 0.08em;
  color: var(--accent);
}
.proc-card__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.25rem, 1.6vw, 1.5rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.proc-card__faces { display: grid; margin: var(--s3) 0 var(--s4); }
.proc-card__face { margin: 0; font-size: var(--t-small); line-height: 1.55; color: var(--ink); }
.proc-card__decides { margin: 0 0 var(--s3); font-size: var(--t-small); line-height: 1.5; color: var(--ink); }
.proc-card__tag {
  display: inline-block;
  margin-right: var(--s2);
  padding: 0.1rem var(--s2);
  border: 1px solid rgba(127, 227, 234, 0.35);
  border-radius: var(--r-1);
  background: rgba(127, 227, 234, 0.12);
  font: 500 var(--t-cite)/1.5 var(--font-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-teal);
}
.proc-card .text-btn { color: var(--accent-teal); }
.proc-card .text-btn:focus-visible { outline-color: var(--accent-teal); }

/* The order record every stage writes to. */
.proc__record {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3) var(--s5);
  margin-top: var(--s6);
  padding-top: var(--s4);
  border-top: 1px solid var(--hairline);
}
.proc__record-name {
  margin: 0;
  font: 500 var(--t-label)/1.3 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}
.proc__chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2); }
.proc__chip {
  position: relative;
  isolation: isolate;
  padding: var(--s1) var(--s3);
  border: 1px solid var(--hairline);
  border-radius: var(--r-2);
  font: 400 var(--t-cite)/1.5 var(--font-mono);
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}
.proc__chip::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: -1;
  border-radius: inherit;
  border: 1px solid rgba(0, 167, 179, 0.8);
  background: rgba(0, 167, 179, 0.16);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out);
}
.proc__chip.is-lit { color: var(--ink); }
.proc__chip.is-lit::before { opacity: 1; }
.no-motion .proc__chip { color: var(--ink); }
.no-motion .proc__chip::before { opacity: 1; }

.proc__close { padding-top: var(--s8); }

/* Phones: the record rides along at the bottom while the cards scroll. */
@media (max-width: 899.98px) {
  .proc__record {
    position: sticky;
    bottom: 0;
    z-index: 2;
    margin-top: var(--s5);
    padding-block: var(--s3);
    background: rgba(10, 23, 25, 0.92);
  }
}

/* Desktop, once scroll.js has pinned it: one row that travels sideways. */
@media (min-width: 900px) {
  .proc--rail .proc__stage {
    height: 100vh;
    margin-top: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: var(--s9) var(--s6);
  }
  .proc--rail .proc__viewport { overflow: clip; }
  .proc--rail .proc__track {
    display: flex;
    gap: var(--s5);
    width: max-content;
    max-width: none;
    margin: 0;
    padding-inline: max(var(--gutter), calc((100vw - var(--shell)) / 2 + var(--gutter)));
    padding-bottom: var(--s6);
  }
  .proc--rail .proc-card { flex: 0 0 clamp(18rem, 27vw, 23rem); }
  .proc--rail .proc-card:not(.is-active) { opacity: 0.55; }
  /* Each card's stub down to the record line. */
  .proc--rail .proc-card::before {
    content: "";
    position: absolute;
    left: var(--s5);
    top: 100%;
    width: 1px;
    height: calc(var(--s6) + 1px);
    background: var(--hairline);
  }
  .proc--rail .proc-card.is-active::before { background: var(--accent); }
  .proc--rail .proc__record { margin-top: 0; }
}
@media (min-width: 900px) and (max-height: 760px) {
  .proc--rail .proc__stage { padding-block: var(--s8) var(--s4); }
  .proc--rail .proc__bar { margin-bottom: var(--s3); }
}

/* ── 3. How we work: the path ─────────────────────────────────────────── */

/* flow-root keeps the steps' top margin inside the wrap. Without it the
   margin collapses out, and the rule's `top` (which counts that margin)
   lands a whole --sp-group below the icons, through the headings. */
.path.steps-wrap { display: flow-root; }
.path .steps { margin-top: var(--s8); }
.path .steps__rule { top: var(--s8); }
.path .step { display: flex; flex-direction: column; }
.path__icon { order: 0; }
.path .step::before { order: 1; margin: var(--s4) 0 var(--s2); }
.path .step__name { order: 2; }
.path .step .body { order: 3; }
.path__promise { margin-top: var(--s8); max-width: 30ch; color: var(--accent); }
.no-motion .path .steps__rule i { transform: none; }

@media (min-width: 900px) {
  .path.steps-wrap { padding-left: 0; }
  .path .steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--s6); }
  .path .step { padding-block: 0; }
  .path .step + .step { border-top: 0; }
  .path .steps__rule {
    left: 1.5rem; right: 1.5rem;
    top: calc(var(--s8) + 1.5rem);
    bottom: auto;
    width: auto;
    height: 1px;
  }
  .path .steps__rule i { transform: scaleX(0); transform-origin: left center; }
}

/* ── Two-column heads for the short sections ──────────────────────────── */

.fit,
.faq { display: grid; gap: var(--s6); }
@media (min-width: 900px) {
  .define { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--s8); align-items: start; }
  .fit,
  .faq { grid-template-columns: minmax(0, 4fr) minmax(0, 8fr); column-gap: var(--s8); align-items: start; }
  .define .chapter-head + *,
  .fit .chapter-head + *,
  .faq .chapter-head + * { margin-top: 0; }
  .parts { padding: var(--s8) var(--s7) var(--s7); }
  .parts__flow { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .parts__flow::before { left: 1.5rem; right: 1.5rem; top: 1.5rem; bottom: auto; width: auto; height: 1px; }
  .parts__flow .part { grid-template-columns: 1fr; }
  .parts__flow .part > * { grid-column: 1; }
  .parts__flow .part__icon { grid-row: auto; margin-bottom: var(--s5); }
  .parts__frame { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── 4. Who it is for ─────────────────────────────────────────────────── */

.fit__text {
  margin: 0;
  max-width: 34ch;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.5rem, 2.6vw, 2.125rem);
  line-height: 1.35;
  letter-spacing: -0.01em;
  font-variation-settings: "opsz" 36;
  text-wrap: pretty;
}
.fit__focus { color: var(--accent); }

/* ── 5. Questions ─────────────────────────────────────────────────────── */

.faq__list { border-top: 1px solid var(--hairline); }
.faq__item { border-bottom: 1px solid var(--hairline); }
.faq__q {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  padding: var(--s5) 0;
  list-style: none;
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.125rem, 1.5vw, 1.375rem);
  line-height: 1.3;
  letter-spacing: -0.005em;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-fast) var(--ease-out);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
  content: "";
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  color: var(--accent);
  background:
    linear-gradient(currentColor, currentColor) center / 100% 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1.5px 100% no-repeat;
  transition: transform var(--dur) var(--ease-out);
}
.faq__item[open] > .faq__q::after { transform: rotate(45deg); }
.faq__q::before {
  content: "";
  position: absolute;
  inset: 0 calc(-1 * var(--s3));
  border-radius: var(--r-2);
  background: rgba(0, 114, 122, 0.06);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .faq__q:hover::before { opacity: 1; }
}
.faq__q:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-2); }
.faq__q:active { transform: translateY(1px); }
.faq__a { padding: 0 0 var(--s5); }
.faq__item[open] .faq__a { animation: faq-in var(--dur) var(--ease-out); }
@keyframes faq-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

/* ── 6. Contact ───────────────────────────────────────────────────────── */

.contact { display: grid; gap: var(--s8); }
.contact__panel {
  padding: var(--s6);
  background: rgba(10, 23, 25, 0.62);
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    0 30px 60px -30px rgba(0, 0, 0, 0.65),
    0 0 60px -30px rgba(0, 167, 179, 0.35);
}
.contact__form { position: relative; display: grid; gap: var(--s5); }
.field { display: grid; gap: var(--s2); }
.field label { font: 500 var(--t-small)/1.4 var(--font-text); color: var(--ink); }
.field input,
.field textarea {
  width: 100%;
  min-height: 3rem;
  padding: var(--s3) var(--s4);
  font: 400 1rem/1.5 var(--font-text);
  color: var(--ink);
  background: rgba(6, 20, 23, 0.7);
  border: 1px solid rgba(230, 238, 238, 0.22);
  border-radius: var(--r-2);
  -webkit-appearance: none;
  appearance: none;
}
.field textarea { min-height: 8.5rem; resize: vertical; }
@media (hover: hover) and (pointer: fine) {
  .field input:hover,
  .field textarea:hover { border-color: rgba(230, 238, 238, 0.4); }
}
.field input:focus-visible,
.field textarea:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 1px; border-color: transparent; }
.field input:active,
.field textarea:active { border-color: rgba(127, 227, 234, 0.5); }
/* Honeypot: out of sight and out of the tab order. */
.hp { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.contact__send { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s5); margin-top: var(--s2); }
.contact__privacy { margin: 0; font-size: var(--t-small); color: var(--ink-soft); }
.contact__error { margin: 0; padding-left: var(--s3); border-left: 2px solid var(--accent-teal); font-size: var(--t-small); color: var(--ink); }
.contact__done { margin: 0; font-family: var(--font-display); font-size: var(--t-quote); line-height: 1.3; color: var(--ink); }
.contact__done:focus { outline: none; }
@media (min-width: 900px) {
  .contact { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); column-gap: var(--s9); align-items: start; }
  .contact__panel { padding: var(--s7); }
}

/* ── 7. Footer ────────────────────────────────────────────────────────── */

.foot { position: relative; z-index: var(--z-copy); padding-block: var(--s7); border-top: 1px solid var(--hairline); }
.foot__line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s2) var(--s3);
  margin: 0;
  font: 400 var(--t-small)/1.6 var(--font-text);
  color: var(--ink-soft);
}
.foot__sep { opacity: 0.5; }
.foot__link { position: relative; color: var(--ink); text-decoration: none; transition: opacity var(--dur-fast) var(--ease-out); }
.foot__link::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .foot__link:hover::after { transform: scaleX(1); }
}
.foot__link:focus-visible { outline: 2px solid var(--accent-teal); outline-offset: 3px; border-radius: var(--r-1); }
.foot__link:active { opacity: 0.7; }

/* ── Privacy page ─────────────────────────────────────────────────────── */

.privacy { padding-top: calc(var(--section) + var(--s9)); }
.privacy__block { margin-top: var(--s7); }
.privacy__block h2 {
  margin: 0 0 var(--s3);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.375rem, 2vw, 1.625rem);
  line-height: 1.2;
  letter-spacing: -0.01em;
}

/* ── Legacy collision ─────────────────────────────────────────────────────
   The older "frosted reading ground" block sets #method to a dark wash with
   a backdrop blur, and an ID selector beats .ground-white. How we work is a
   white section on this page, so put it back. about.html has no #method. */
#method { background: var(--surface); }
#method::before { content: none; }

/* Stage numbers sit on dark cards, and the void accent is too dim there. */
.proc-card__n { color: var(--accent-teal); }

/* ═══════════════════════════════════════════════════════════════════════
   THE FLOW PASS (2026-10-22, second pass)
   Nekunj: the white sections read as a document, and as separate bands.
   This gives the page one voice and one line through it: centred heads in
   the hero's bold Inter, a teal thread drawn from section to section, one
   continuous light ground per white run, and three things that move (the
   inside-one diagram, the example vignettes, the read-along statements).
   Appended last; anything touching classes that about.html or
   privacy.html share is scoped to body.home.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── One ground per white run ─────────────────────────────────────────── */

.run {
  position: relative;
  background:
    radial-gradient(56rem 38rem at 88% 6%, rgba(0, 167, 179, 0.08), transparent 70%),
    radial-gradient(48rem 34rem at 6% 42%, rgba(127, 227, 234, 0.14), transparent 70%),
    radial-gradient(52rem 36rem at 92% 88%, rgba(0, 114, 122, 0.07), transparent 70%),
    var(--paper);
}
.run > .band { background: transparent; }
/* The legacy frosted block gives #method a background by ID. */
#method { background: transparent; }

/* ── Hero, centred ────────────────────────────────────────────────────── */

.hero__inner { text-align: center; }
.hero-head,
.hero__sub { margin-inline: auto; }

/* ── One voice: section heads in the hero's Inter, centred ────────────── */

.home .chapter-head {
  font-family: var(--font-text);
  font-weight: 600;
  font-size: clamp(2.375rem, 5.2vw, 4.75rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  font-variation-settings: normal;
  max-width: 17ch;
  margin-inline: auto;
  text-align: center;
}

/* ── The thread ───────────────────────────────────────────────────────── */

.home .thread {
  position: absolute;
  z-index: 1;
  left: 50%;
  top: calc(-1 * var(--section));
  width: 1px;
  height: calc(2 * var(--section) - var(--s4));
  margin-left: -0.5px;
  pointer-events: none;
}
.home .thread i {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(0, 167, 179, 0), var(--accent-void) 40%);
  transform-origin: top center;
}
.home .thread b {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 9px;
  height: 9px;
  margin: 0 0 -4px -4.5px;
  border-radius: 50%;
  background: var(--accent-void);
  box-shadow: 0 0 0 4px rgba(0, 167, 179, 0.16), 0 0 16px rgba(0, 167, 179, 0.55);
}
@media (prefers-reduced-motion: no-preference) {
  .reveal-wait .thread { opacity: 0; }
}

/* ── 2A. What it is: a spoken statement ───────────────────────────────── */

.define { display: block; text-align: center; }
.define .define__text {
  margin: var(--s7) auto 0;
  max-width: 21ch;
  font-size: clamp(2rem, 4.4vw, 4rem);
  line-height: 1.12;
  letter-spacing: -0.018em;
  font-variation-settings: "opsz" 72;
}
.define .define__more { margin: var(--s7) auto 0; max-width: 46ch; }
.define .define__approve {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  margin: var(--s5) 0 0;
  padding: var(--s2) var(--s4) var(--s2) var(--s3);
  border: 1px solid rgba(0, 114, 122, 0.22);
  border-radius: 999px;
  background: rgba(0, 167, 179, 0.07);
  font: 500 var(--t-small)/1.4 var(--font-text);
  color: var(--accent);
}
.define__approve .icon { width: 1.125rem; height: 1.125rem; }

/* ── 2B. What is inside one: the live diagram ─────────────────────────── */

.parts__flow::before { content: none; }
.parts__run { position: relative; }

/* The wire, vertical through the icons on phones. */
.parts__wire {
  position: absolute;
  left: 1.5rem;
  top: 1.5rem;
  bottom: 1.5rem;
  width: 1px;
  background: var(--hairline);
}
.parts__wire i {
  position: absolute;
  inset: 0;
  background: var(--accent-void);
  transform-origin: top center;
}
/* The pulse: a full-size box whose dot sits at the wire's start, so a
   translate of 100% carries the dot exactly the wire's length. */
.parts__wire b { position: absolute; inset: 0; opacity: 0; }
.parts__wire b::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  border-radius: 50%;
  background: var(--accent-void);
  box-shadow: 0 0 0 4px rgba(0, 167, 179, 0.18), 0 0 18px rgba(0, 167, 179, 0.6);
}
.parts.is-flowing .parts__wire b { animation: wire-run-y 2.8s cubic-bezier(0.45, 0, 0.55, 1) infinite; }
@keyframes wire-run-y {
  0%       { transform: translateY(0); opacity: 0; }
  12%, 88% { opacity: 1; }
  100%     { transform: translateY(100%); opacity: 0; }
}
@keyframes wire-run-x {
  0%       { transform: translateX(0); opacity: 0; }
  12%, 88% { opacity: 1; }
  100%     { transform: translateX(100%); opacity: 0; }
}
@media (min-width: 900px) {
  /* From the first icon's centre to the fourth's: column four starts at
     75% + 0.75 of the column gap, and the icon centre is 1.5rem in. */
  .parts__wire {
    left: 1.5rem;
    right: calc(25% - 0.75 * var(--s6) - 1.5rem);
    top: 1.5rem;
    bottom: auto;
    width: auto;
    height: 1px;
  }
  .parts__wire i { transform-origin: left center; }
  .parts__wire b::before { left: 0; top: 50%; }
  .parts.is-flowing .parts__wire b { animation-name: wire-run-x; }
}

/* Parts wait dimmed until the wire reaches them (scroll.js parts()). */
.parts--live .part { opacity: 0.38; transition: opacity 480ms var(--ease-out); }
.parts--live .part.is-on { opacity: 1; }

/* The lit ring behind an icon. Shared by the diagram and the path. */
.part__icon::before,
.path__icon::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(0, 167, 179, 0.22), rgba(0, 167, 179, 0.08));
  box-shadow: 0 0 0 6px rgba(0, 167, 179, 0.10), 0 10px 28px -8px rgba(0, 167, 179, 0.55);
  opacity: 0;
  transform: scale(0.7);
  transition: opacity 480ms var(--ease-out), transform 480ms var(--ease-out);
}
.part.is-on .part__icon::before,
.step.is-on .path__icon::before { opacity: 1; transform: none; }
.part__icon .icon,
.path__icon .icon { position: relative; }

/* ── 2C. Example vignettes ────────────────────────────────────────────── */

.vig {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s2);
  height: 10.5rem;
  margin: 0 0 var(--s5);
  padding: var(--s4);
  overflow: hidden;
  border: 1px solid var(--hairline);
  border-radius: var(--r-3);
  background:
    radial-gradient(80% 90% at 100% 0%, rgba(127, 227, 234, 0.18), transparent 70%),
    linear-gradient(180deg, #F2F7F7, #FFFFFF);
  font: 500 0.8125rem/1.3 var(--font-text);
  color: var(--ink);
}
.vig p { margin: 0; }
.vig ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s2); }
.vig .icon { width: 0.875rem; height: 0.875rem; }

/* Example 1: an order checked, then approved. */
.vig__mail {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  background: #FFFFFF;
  border: 1px solid var(--hairline);
  border-radius: var(--r-2);
  box-shadow: 0 6px 16px -10px rgba(0, 60, 66, 0.35);
}
.vig__mail .icon { width: 1rem; height: 1rem; color: var(--accent); }
.vig__checks li { display: flex; align-items: center; gap: var(--s2); color: var(--ink-soft); }
.vig__tick {
  position: relative;
  flex: 0 0 auto;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  background: var(--accent-void);
}
.vig__tick::after {
  content: "";
  position: absolute;
  left: 0.3rem;
  top: 0.17rem;
  width: 0.22rem;
  height: 0.42rem;
  border: solid #FFFFFF;
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.vig__approve {
  position: absolute;
  right: var(--s4);
  bottom: var(--s4);
  padding: var(--s1) var(--s3);
  border-radius: 999px;
  background: var(--accent-paper);
  color: #FFFFFF;
  font-weight: 600;
}
.vig__approve::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 0 2px var(--accent-void);
  opacity: 0;
}

/* Example 2: things watched, one flagged. */
.vig__rows li {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s3);
  background: #FFFFFF;
  border: 1px solid var(--hairline);
  border-radius: var(--r-2);
}
.vig__dot { flex: 0 0 auto; width: 0.5rem; height: 0.5rem; border-radius: 50%; background: #9FB3B5; }
.vig__rows .vig__alert { border-color: rgba(0, 167, 179, 0.55); box-shadow: 0 0 0 3px rgba(0, 167, 179, 0.10); }
.vig__alert .vig__dot { background: var(--accent-void); }
.vig__flag { margin-left: auto; display: inline-flex; align-items: center; gap: 0.25rem; font-size: 0.75rem; color: var(--accent); }
.vig__pill {
  align-self: flex-start;
  padding: var(--s1) var(--s3);
  border-radius: 999px;
  background: var(--ink);
  color: #FFFFFF;
}

/* Example 3: a question answered from your own documents. */
.vig__q {
  align-self: flex-end;
  max-width: 85%;
  padding: var(--s2) var(--s3);
  border-radius: 12px 12px 2px 12px;
  background: var(--accent-paper);
  color: #FFFFFF;
}
.vig__a {
  align-self: flex-start;
  width: 78%;
  display: grid;
  gap: 0.4rem;
  padding: var(--s3);
  border-radius: 12px 12px 12px 2px;
  background: #FFFFFF;
  border: 1px solid var(--hairline);
}
.vig__bar { display: block; height: 0.4rem; border-radius: 999px; background: #DCE6E6; }
.vig__bar--short { width: 60%; }
.vig__src { align-self: flex-start; display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.75rem; color: var(--accent); }

/* The loop. Only once ui.js has set .vig-ready (JS on, motion allowed),
   and only while the card is on screen (.is-playing). Each step arrives
   at its own point of one 7s cycle and everything clears together. */
@keyframes vig-1 { 0%, 4%  { opacity: 0; transform: translateY(6px); } 10%, 88% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes vig-2 { 0%, 18% { opacity: 0; transform: translateY(6px); } 24%, 88% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes vig-3 { 0%, 32% { opacity: 0; transform: translateY(6px); } 38%, 88% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes vig-4 { 0%, 46% { opacity: 0; transform: translateY(6px); } 52%, 88% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes vig-5 { 0%, 60% { opacity: 0; transform: scale(0.9); } 66%, 88% { opacity: 1; transform: none; } 96%, 100% { opacity: 0; transform: none; } }
@keyframes vig-ring { 0% { opacity: 0.7; transform: scale(1); } 100% { opacity: 0; transform: scale(1.35); } }
@keyframes vig-bell {
  0%, 60%, 100% { transform: rotate(0); }
  66% { transform: rotate(14deg); }
  74% { transform: rotate(-12deg); }
  82% { transform: rotate(8deg); }
  90% { transform: rotate(-4deg); }
}
.vig-ready .vig [data-step] { animation: 7s var(--ease-out) infinite paused; }
.vig-ready .vig [data-step="1"] { animation-name: vig-1; }
.vig-ready .vig [data-step="2"] { animation-name: vig-2; }
.vig-ready .vig [data-step="3"] { animation-name: vig-3; }
.vig-ready .vig [data-step="4"] { animation-name: vig-4; }
.vig-ready .vig [data-step="5"] { animation-name: vig-5; }
.vig-ready .vig.is-playing [data-step] { animation-play-state: running; }
.vig-ready .vig.is-playing .vig__approve::after { animation: vig-ring 1.6s var(--ease-out) infinite; }
.vig-ready .vig.is-playing .vig__flag .icon { transform-origin: 50% 10%; animation: vig-bell 1.8s ease-in-out infinite; }

/* ── 2C, continued. Process head, centred ─────────────────────────────── */

.proc__head { text-align: center; }
.proc__head .lead { margin-inline: auto; }
.proc__head .proc__note { margin-inline: auto; text-align: left; }

/* ── 3. How we work ───────────────────────────────────────────────────── */

.home .path .step__name {
  font-family: var(--font-text);
  font-weight: 600;
  font-size: clamp(1.5rem, 2.3vw, 2.125rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
  font-variation-settings: normal;
}
.home .path__promise {
  margin: var(--s9) auto 0;
  max-width: 22ch;
  text-align: center;
  font-size: clamp(1.75rem, 3.6vw, 3.25rem);
  line-height: 1.15;
}

/* ── 4. Who it is for: a spoken statement ─────────────────────────────── */

.fit { display: block; text-align: center; }
.fit .fit__text {
  margin: var(--s7) auto 0;
  max-width: 24ch;
  font-size: clamp(2rem, 4.2vw, 3.75rem);
  line-height: 1.14;
  letter-spacing: -0.018em;
  font-variation-settings: "opsz" 72;
}
.fit .fit__ask { margin: var(--s6) auto 0; max-width: 48ch; }

/* ── 5. Questions, 6. Contact: one centred column ─────────────────────── */

.faq { display: block; }
.faq .faq__list { width: 100%; max-width: 52rem; margin: var(--s7) auto 0; }

.contact { display: block; }
.contact__intro { text-align: center; }
.contact__intro .lead { margin: var(--s5) auto 0; }
.contact .contact__panel { max-width: 44rem; margin: var(--s7) auto 0; }

/* Parts of different lengths share one row. Without this the shorter ones
   are stretched by the grid and spread the extra height across their rows,
   so their numbers and titles sit lower than the tallest part's. */
.parts__flow .part,
.parts__frame .part { align-content: start; }

/* ═══════════════════════════════════════════════════════════════════════
   PHONE RAIL AND ABOUT (2026-10-22, third pass)
   The process rail now pins on phones too (scroll.js proc()), with the
   detail moved into a bottom sheet so a pinned card always fits a small
   screen. About is rebuilt in the homepage's grammar; body.home now means
   "a page in the homepage's voice", not literally the homepage.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── The process rail on phones ───────────────────────────────────────── */

.proc__now { display: none; }

@media (max-width: 899.98px) and (min-height: 560px) {
  .proc--rail .proc__stage {
    height: 100svh;
    margin-top: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-block: calc(var(--s8) + var(--s3)) var(--s4);
  }
  .proc--rail .proc__bar { margin-bottom: var(--s4); }
  .proc--rail .proc__viewport { overflow: clip; }
  .proc--rail .proc__track {
    --card-w: min(82vw, 21rem);
    display: flex;
    align-items: stretch;
    gap: var(--s3);
    width: max-content;
    max-width: none;
    margin: 0;
    padding: 0 calc((100vw - var(--card-w)) / 2);
  }
  .proc--rail .proc-card { flex: 0 0 var(--card-w); padding: var(--s4) var(--s4) var(--s3); }
  .proc--rail .proc-card:not(.is-active) { opacity: 0.45; }
  /* The detail lives in the sheet here, never in the card. */
  .proc--rail .proc-card__more { display: none; }
  .proc--rail .is-open .proc-card__sum { opacity: 1; visibility: visible; transform: none; }

  /* The order record becomes one slim bar of seven, plus the field being
     written. The chip names stay in the markup for screen readers. */
  .proc--rail .proc__record {
    position: static;
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: baseline;
    gap: var(--s2) var(--s3);
    margin-top: var(--s5);
    padding: 0 var(--gutter);
    border-top: 0;
    background: none;
  }
  .proc--rail .proc__now {
    display: block;
    justify-self: end;
    margin: 0;
    font: 500 var(--t-label)/1.3 var(--font-mono);
    letter-spacing: 0.08em;
    color: var(--accent-teal);
  }
  .proc--rail .proc__chips { grid-column: 1 / -1; flex-wrap: nowrap; gap: 4px; }
  .proc--rail .proc__chip {
    flex: 1 1 0;
    height: 4px;
    padding: 0;
    overflow: hidden;
    border: 0;
    border-radius: 2px;
    background: rgba(230, 238, 238, 0.14);
    font-size: 0;
    line-height: 0;
    color: transparent;
  }
  .proc--rail .proc__chip::before { inset: 0; border: 0; background: var(--accent-void); }
}

/* ── The bottom sheet (phones) ────────────────────────────────────────── */

.proc-sheet {
  position: fixed;
  inset: auto 0 0 0;
  width: 100%;
  max-width: none;
  max-height: 82svh;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  color: var(--ink);
  background: #0E2124;
  border: 1px solid var(--hairline);
  border-bottom: 0;
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -24px 60px -20px rgba(0, 0, 0, 0.7), 0 -1px 0 rgba(127, 227, 234, 0.12);
}
.proc-sheet[open] { animation: sheet-up 380ms var(--ease-out); }
@keyframes sheet-up { from { transform: translateY(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .proc-sheet[open] { animation: none; }
}
.proc-sheet::backdrop { background: rgba(6, 20, 23, 0.62); }
.proc-sheet__body { padding: var(--s3) var(--gutter) calc(var(--s6) + env(safe-area-inset-bottom)); }
.proc-sheet__grip {
  display: block;
  width: 2.5rem;
  height: 4px;
  margin: 0 auto var(--s5);
  border-radius: 2px;
  background: rgba(230, 238, 238, 0.25);
}
.proc-sheet__n {
  margin: 0 0 var(--s2);
  font: 400 var(--t-cite)/1.4 var(--font-mono);
  letter-spacing: 0.08em;
  color: var(--accent-teal);
}
.proc-sheet__title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1.2;
  letter-spacing: -0.01em;
}
.proc-sheet__text { margin: var(--s4) 0 0; font-size: var(--t-body); line-height: 1.6; color: var(--ink); }
.proc-sheet .proc-sheet__decides { margin: var(--s4) 0 0; }
.proc-sheet__close { width: 100%; margin-top: var(--s6); justify-content: center; }
html.sheet-open { overflow: hidden; }

/* ── About: the homepage's grammar ────────────────────────────────────── */

.home .letter { margin-inline: auto; }
.home .signoff { max-width: 62ch; margin-inline: auto; }
.hero .byline { margin: var(--s6) 0 0; }

/* A big centred spoken statement, the same voice as the homepage's
   definition and Who it is for. */
.home .statement {
  margin: var(--s7) auto 0;
  max-width: 24ch;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.875rem, 3.8vw, 3.25rem);
  line-height: 1.14;
  letter-spacing: -0.018em;
  font-variation-settings: "opsz" 72;
  text-align: center;
  text-wrap: balance;
}

/* ── About #build: systems and integration ────────────────────────────── */

.build {
  display: grid;
  gap: var(--s7);
  margin-top: var(--s8);
  align-items: center;
}
.build__side { max-width: 46ch; margin-inline: auto; }
.build__name {
  margin: 0 0 var(--s3);
  font-family: var(--font-text);
  font-weight: 600;
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  line-height: 1.05;
  letter-spacing: -0.03em;
}
@media (min-width: 900px) {
  .build { grid-template-columns: minmax(0, 1fr) minmax(0, 30rem) minmax(0, 1fr); gap: var(--s7); }
  .build__side { margin: 0; }
}

/* The integration map: a system in the middle, the tools a business
   already runs around it, joined by spokes that draw in on scroll
   (scroll.js hub()) and then carry a pulse in from each tool. */
.hub {
  position: relative;
  width: min(100%, 30rem);
  aspect-ratio: 1;
  margin: 0 auto;
}
.hub__ring { position: absolute; inset: 0; }
.hub__spoke {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 40%;
  height: 1px;
  transform-origin: 0 50%;
  transform: rotate(var(--a));
  background: var(--hairline);
}
.hub__spoke i {
  position: absolute;
  inset: 0;
  background: var(--accent-void);
  transform-origin: left center;
}
.hub__spoke b { position: absolute; inset: 0; opacity: 0; }
/* The pulse box sits on the spoke with its dot at the tool end, and slides
   back toward the middle. Sliding it the other way would hang the box past
   the map's right edge for a moment and widen the page on a phone. */
.hub__spoke b::before {
  content: "";
  position: absolute;
  left: 100%;
  top: 50%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--accent-void);
  box-shadow: 0 0 0 4px rgba(0, 167, 179, 0.16), 0 0 14px rgba(0, 167, 179, 0.6);
}
.hub.is-live .hub__spoke b { animation: hub-in 2.6s cubic-bezier(0.45, 0, 0.55, 1) var(--d) infinite; }
@keyframes hub-in {
  0%       { transform: translateX(0); opacity: 0; }
  15%, 80% { opacity: 1; }
  100%     { transform: translateX(-100%); opacity: 0; }
}
.hub__core {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 1;
  display: grid;
  place-items: center;
  width: 8rem;
  height: 8rem;
  margin: -4rem 0 0 -4rem;
  padding: var(--s3);
  border-radius: 50%;
  text-align: center;
  font: 600 1rem/1.15 var(--font-text);
  letter-spacing: -0.01em;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid rgba(0, 167, 179, 0.45);
  box-shadow: 0 0 0 8px rgba(0, 167, 179, 0.08), 0 18px 44px -16px rgba(0, 114, 122, 0.45);
}
.hub__tools { list-style: none; margin: 0; padding: 0; }
/* Zero-size anchors at each spoke's end; the chip centres on it. */
.hub__tool {
  position: absolute;
  left: var(--x);
  top: var(--y);
  z-index: 1;
  width: 0;
  height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.hub__tool span {
  flex: 0 0 auto;
  padding: var(--s2) var(--s3);
  white-space: nowrap;
  font: 500 var(--t-small)/1 var(--font-text);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  box-shadow: 0 1px 2px rgba(0, 40, 44, 0.06), 0 10px 22px -12px rgba(0, 114, 122, 0.35);
}
@media (max-width: 479.98px) {
  .hub__core { width: 6.5rem; height: 6.5rem; margin: -3.25rem 0 0 -3.25rem; font-size: 0.875rem; }
  .hub__tool span { padding: 0.4rem var(--s2); font-size: 0.8125rem; }
}

/* ── About #ahead: where this is heading ─────────────────────────────────── */

.ahead { min-height: 100svh; display: grid; align-items: center; }
.home .colossal { max-width: 13ch; margin-inline: auto; text-align: center; }
.ahead__copy {
  display: grid;
  gap: var(--s5);
  max-width: 50ch;
  margin: var(--s8) auto 0;
  text-align: center;
}
.ahead__copy .lead,
.ahead__copy .body { margin: 0 auto; }
.ahead__copy .pull { margin: var(--s3) auto 0; color: var(--accent-teal); }

/* ── About #close ─────────────────────────────────────────────────────── */

.about-end { text-align: center; }
.about-end .lead { margin: var(--s5) auto 0; }
.about-end__cta { margin: var(--s7) 0 0; }

/* ═══ Homepage #what: the surface lifts ═════════════════════════════════
   Static by default (no JS, reduced motion, under 520px tall): a white
   block carrying the question, a glowing seam, then the dark block. With
   .lift--live (scroll.js lift()) the stage is one pinned screen: the
   sheet covers the line, then translates up and off. Only transform and
   opacity animate. No transform, filter or will-change on .lift or
   .lift__stage: the stage is pinned. */

.lift__stage { position: relative; }

.lift__sheet {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  min-height: 70svh;
  padding: var(--section) var(--gutter);
  background:
    radial-gradient(56rem 38rem at 88% 6%, rgba(0, 167, 179, 0.08), transparent 70%),
    radial-gradient(48rem 34rem at 6% 42%, rgba(127, 227, 234, 0.14), transparent 70%),
    var(--paper);
  /* The shadow the lifting page casts on the field below it. */
  box-shadow:
    0 2px 6px rgba(0, 30, 34, 0.18),
    0 48px 90px -40px rgba(0, 30, 34, 0.65);
}
/* The page's edge: a thin teal line with a glow, brightest in the middle. */
.lift__sheet::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: linear-gradient(90deg,
    rgba(0, 167, 179, 0) 0%,
    var(--accent-void) 28%,
    var(--accent-teal) 50%,
    var(--accent-void) 72%,
    rgba(0, 167, 179, 0) 100%);
  box-shadow:
    0 0 14px 1px rgba(0, 167, 179, 0.75),
    0 0 48px 6px rgba(0, 167, 179, 0.22);
  pointer-events: none;
}

/* The question. Inter 700, the one place on the site above 600, on
   Nekunj's ask ("a bit bolder"). */
.lift__ask {
  margin: 0 auto;
  max-width: 12ch;
  font-family: var(--font-text);
  font-weight: 700;
  font-size: clamp(3rem, 8.6vw, 8.25rem);
  line-height: 0.94;
  letter-spacing: -0.045em;
  text-align: center;
  text-wrap: balance;
  color: var(--ink);
}
.lw { display: inline-block; }

.lift__under { padding-top: var(--section); }
.lift__head {
  margin: 0 auto;
  max-width: 15ch;
  font-family: var(--font-text);
  font-weight: 600;
  font-size: clamp(2.75rem, 6.6vw, 6.5rem);
  line-height: 0.96;
  letter-spacing: -0.035em;
  text-align: center;
  text-wrap: balance;
  color: var(--ink);
}
.lift__head em { font-style: normal; color: var(--accent); }
.wi--key { color: var(--accent); }

.lift__after { padding-block: var(--s8) var(--section); }
.lift__explain { margin: 0 auto; max-width: 52ch; text-align: center; }
.lift__after .define__text { margin-top: var(--s9); }
.lift .define__approve {
  border-color: rgba(0, 167, 179, 0.32);
  background: rgba(0, 167, 179, 0.10);
}

/* Live: one pinned screen. */
.lift--live .lift__stage {
  height: 100svh;
  display: grid;
  place-items: center;
  overflow: clip;
}
.lift--live .lift__sheet {
  position: absolute;
  inset: 0;
  min-height: 0;
}
.lift--live .lift__under { padding-top: 0; }

@media (prefers-reduced-motion: no-preference) {
  .reveal-wait .lift__ask,
  .reveal-wait .lift__head { opacity: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   THE RHYTHM PASS (2026-10-24)
   Nekunj: gaps between elements were uneven, and phones were cluttered.
   Every gap on the homepage is now one of five steps, picked by the
   relationship between the two things, never by eye:

     --sp-text   16 to 24   paragraph to paragraph inside one group
     --sp-head   24 to 48   a heading or statement to the text answering it
     --sp-group  56 to 88   one group to the next: text to a component,
                            a panel, a button, a list or a new statement
     --sp-card   20 to 40   padding inside a card or panel
     --section   88 to 144  a section's own top and bottom padding

   Widened the same day, at Nekunj's ask ("everything needs more room"):
   the first values were 20/32, 48/64 and 80/120.

   Items inside one component (parts, steps) sit var(--s6) apart.
   Scoped to body.index (the homepage). About gets the tokens only.
   Appended last on purpose: it has to win the cascade.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  --sp-text:  clamp(1rem, 0.7rem + 1.2vw, 1.5rem);
  --sp-head:  clamp(1.5rem, 1.1rem + 2.4vw, 3rem);
  --sp-group: clamp(3.5rem, 2.5rem + 3.2vw, 5.5rem);
  --sp-card:  clamp(1.25rem, 0.75rem + 2vw, 2.5rem);
  --section:  clamp(5.5rem, 4.5rem + 5vw, 9rem);

  --t-statement: clamp(2rem, 1.3rem + 3vw, 3.5rem);
  --t-card:      clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
  --measure-lead: 48ch;
}

/* ── One measure, one statement size, one card-title size ─────────────── */

.index .hero__sub,
.index .lift__explain,
.index .define .define__more,
.index .proc__head .lead,
.index .fit .fit__ask,
.index .contact__intro .lead { max-width: var(--measure-lead); }

.index .define .define__text,
.index .fit .fit__text,
.index .path__promise {
  max-width: 24ch;
  margin-inline: auto;
  font-size: var(--t-statement);
  line-height: 1.14;
  letter-spacing: -0.018em;
}

.index .part__title,
.index .eg__title,
.index .proc-card__title { font-size: var(--t-card); }

/* ── Hero ─────────────────────────────────────────────────────────────── */

.index .hero__sub { margin-top: var(--sp-head); }
.index .hero__cta { margin-top: var(--sp-group); }

/* ── 2A. What is an AI system? ────────────────────────────────────────── */

/* (The definition, the explanation and their spacing now live in the
   sequence pass at the end of this file.) */

/* ── Section heads and the component under them ──────────────────────── */

.index .chapter-head + .lead { margin-top: var(--sp-head); }

#inside .parts,
#examples .eg-list,
.index .path .steps,
.index .faq .faq__list { margin-top: var(--sp-group); }

.index .eg { padding: var(--sp-card); }

/* ── 2C, continued. The process ───────────────────────────────────────── */

/* The note is the one line that tells a reader every business is
   different, so it is a lit panel, not a grey aside, and (sequence pass)
   it gets the middle of the pinned screen to itself. */
.index .proc__note {
  max-width: 44rem;
  margin: 0 auto;
  padding: var(--sp-card);
  border: 1px solid rgba(0, 167, 179, 0.55);
  border-radius: var(--r-3);
  background:
    linear-gradient(180deg, rgba(0, 167, 179, 0.16), rgba(0, 167, 179, 0.05)),
    rgba(10, 23, 25, 0.72);
  box-shadow:
    inset 0 1px 0 rgba(127, 227, 234, 0.16),
    0 0 0 4px rgba(0, 167, 179, 0.07),
    0 0 48px -12px rgba(0, 167, 179, 0.45),
    0 28px 60px -32px rgba(0, 0, 0, 0.7);
  font-family: var(--font-text);
  font-style: normal;
  font-weight: 400;
  font-size: var(--t-lead);
  line-height: 1.5;
  text-align: center;
  text-wrap: pretty;
  color: var(--ink);
}
.index .proc__note strong {
  display: block;
  margin-bottom: var(--s2);
  font-weight: 600;
  font-size: clamp(1.25rem, 1rem + 1vw, 1.75rem);
  line-height: 1.2;
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--accent-teal);
}

.index .proc__close .pull { max-width: 52ch; margin-inline: auto; text-align: center; }

/* Docking the rail. The stage is one screen tall and centres its content
   between --pt and --pb, so the empty stage above and below the content
   is --slack each. scroll.js proc() writes the content's height to
   --proc-h (the rail frame's height). The closing quote is pulled in so
   it sits one --sp-group below the rail (sequence pass, end of file). */
@media (min-width: 900px) {
  .index .proc--rail { --stage-h: 100vh; --pt: var(--s9); --pb: var(--s6); }
}
@media (min-width: 900px) and (max-height: 760px) {
  .index .proc--rail { --pt: var(--s8); --pb: var(--s4); }
}
@media (max-width: 899.98px) and (min-height: 560px) {
  .index .proc--rail { --stage-h: 100svh; --pt: calc(var(--s8) + var(--s3)); --pb: var(--s4); }
}
.index .proc--rail {
  --slack: max(0px, (var(--stage-h, 100vh) - var(--pt, 0px) - var(--pb, 0px) - var(--proc-h, var(--stage-h, 100vh))) / 2);
}
/* ── 3. How we work, 4. Who it is for, 5. Questions, 6. Contact ──────── */

.index .path__promise { margin-top: var(--sp-group); }
@media (min-width: 900px) {
  .index .path .steps__rule { top: calc(var(--sp-group) + 1.5rem); }
}

.index .fit .fit__text { margin-top: var(--sp-head); }
.index .fit .fit__ask { margin-top: var(--sp-head); }

.index .contact .contact__panel { margin-top: var(--sp-group); padding: var(--sp-card); }

/* ── Phones and small tablets ─────────────────────────────────────────── */

@media (max-width: 899.98px) {
  .index .part__icon,
  .index .path__icon { width: 2.5rem; height: 2.5rem; }

  /* What is inside one: the same box, less padding, smaller icons. */
  #inside .parts { padding: var(--s6) var(--s4) var(--s5); }
  .index .parts__name { left: var(--s4); }
  .index .part { grid-template-columns: 2.5rem minmax(0, 1fr); column-gap: var(--s4); }
  .index .parts__wire { left: 1.25rem; top: 1.25rem; bottom: 1.25rem; }
  .index .parts__frame { margin-top: var(--s6); }

  /* How we work: rows, the icon on the left, words on the right, joined
     by a line through the icons. Each row draws its own segment down to
     the next icon (CSS only, so reduced motion gets it too); a segment
     lights when the step below it lights (scroll.js sequence()). */
  .index .path .steps__rule { display: none; }
  .index .path .step {
    position: relative;
    display: grid;
    grid-template-columns: 2.5rem minmax(0, 1fr);
    column-gap: var(--s4);
    align-items: start;
    padding: 0 0 var(--s6);
  }
  .index .path .step:last-child { padding-bottom: 0; }
  .index .path .step + .step { border-top: 0; }
  .index .path__icon { grid-column: 1; grid-row: 1 / span 3; }
  .index .path .step::before {
    grid-column: 2;
    grid-row: 1;
    margin: 0 0 var(--s1);
    font: 400 var(--t-cite)/1.4 var(--font-mono);
    font-variation-settings: normal;
    letter-spacing: 0.08em;
    color: var(--accent);
  }
  .index .path .step__name { grid-column: 2; grid-row: 2; margin: 0 0 var(--s2); }
  .index .path .step .body { grid-column: 2; grid-row: 3; }
  .index .path .step:not(:last-child) {
    background: linear-gradient(var(--hairline), var(--hairline)) no-repeat calc(1.25rem - 0.5px) 2.5rem / 1px 100%;
  }
  .index .path .step:not(:last-child)::after {
    content: "";
    position: absolute;
    left: calc(1.25rem - 0.5px);
    top: 2.5rem;
    bottom: 0;
    width: 1px;
    background: var(--accent-void);
    transform: scaleY(0);
    transform-origin: top center;
    transition: transform 480ms var(--ease-out);
  }
  .index .path .step:has(+ .step.is-on)::after,
  .no-motion .index .path .step::after { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   THE SEQUENCE PASS (2026-10-24, third pass)
   Nekunj: a longer page with more room is worse. Whatever text is current
   belongs in the middle of the screen, where the eye lands, and the next
   piece is produced in its place. So #what and #process are each one
   pinned screen holding a few frames in the same grid cell; scroll.js
   fades one out and produces the next. Static by default (no JS, reduced
   motion, short landscape phones): the frames are plain stacked blocks.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Frames, static ───────────────────────────────────────────────────── */

.index .lift__frame + .lift__frame,
.index .proc__frame + .proc__frame { margin-top: var(--sp-group); }
.index .lift__stage { padding-bottom: var(--section); }

/* The explanation is alone on its screen, so it is set larger than a lead. */
.index .lift__tell .lift__explain {
  max-width: var(--measure-lead);
  margin: 0 auto;
  font-size: clamp(1.25rem, 0.95rem + 1.3vw, 1.75rem);
  line-height: 1.45;
  color: var(--ink);
}

/* Inside the definition frame the statement leads; no top margin. */
.index .define .define__text { margin-top: 0; }
.index .define .define__more { margin-top: var(--sp-head); }
.index .define .define__approve { margin-top: var(--sp-text); }

.index .proc__close { position: relative; z-index: 2; padding-top: var(--sp-group); }

/* ── Frames, live: one cell, every frame centred ─────────────────────── */

.index .lift--live .lift__stage { padding-bottom: 0; }
.index .lift--live .lift__frame {
  grid-area: 1 / 1;
  align-self: center;
  margin-top: 0;
}

/* NOT flex any more: the old rule made this a flex column, and a grid with
   justify-content: center would shrink its one implicit column to the
   widest content. An explicit full-width column and stretch fix that. */
.index .proc--rail { padding-top: 0; }
.index .proc--rail .proc__stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-content: stretch;
  align-content: center;
  margin-top: 0;
  padding-block: var(--pt) var(--pb);
}
.index .proc--rail .proc__frame {
  grid-area: 1 / 1;
  align-self: center;
  margin-top: 0;
}
.index .proc--rail .proc__close {
  padding-top: 0;
  margin-top: calc(var(--sp-group) - var(--pb) - var(--slack));
}

/* The thread used to end just above the heading, which sat one --section
   below the section's top. The heading now sits in the middle of the
   pinned screen, so the thread is as long as the distance to it.
   scroll.js proc() writes --intro-h (the heading frame's height). */
.index .proc--rail > .thread {
  height: calc(var(--section) + var(--pt) + (var(--stage-h) - var(--pt) - var(--pb)) / 2 - var(--intro-h, 0px) / 2 - var(--s4));
}

/* The frames wait hidden until scroll.js has written their start states. */
@media (prefers-reduced-motion: no-preference) {
  .reveal-wait .lift__tell,
  .reveal-wait .lift__define,
  .reveal-wait .proc__frame--note { opacity: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════
   THE BEATS PASS (2026-10-24, fourth pass)
   #what and #process pin as a few beats that play on their own clock
   (scroll.js stepPin). Touch screens hold the page still and play one beat
   per swipe (the lock, 2026-10-25); there is no CSS scroll snap, which
   grabbed the page near every stop and fought the beats. Phones swipe the
   process rail with the thumb. Desktop wheel steps by distance (Lenis).
   ═══════════════════════════════════════════════════════════════════════ */

.index .lift,
.index .proc { position: relative; }

/* The lock (touch screens, scroll.js stepPin): the page holds still
   while a stepped section plays one beat per swipe. */
html.beats-lock,
html.beats-lock body {
  overflow: hidden;
  overscroll-behavior: none;
  /* The browser must not pan the page at all while it is held (iOS can
     ignore overflow: hidden): every vertical swipe belongs to stepPin. */
  touch-action: none;
}

/* The process rail is a thumb-driven carousel on any touch screen
   (.proc--swipe is only ever set by scroll.js). Overrides the clip from
   the phone rail pass. */
.proc--rail.proc--swipe .proc__viewport {
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.proc--swipe .proc__viewport::-webkit-scrollbar { display: none; }
/* Sideways drags on the rail always go to the rail, however diagonal the
   thumb; vertical ones are left to stepPin. Without this a slightly
   diagonal start can commit to the (blocked) page scroll and the rail sits
   still until the reader tries again. */
.proc--swipe .proc__viewport { touch-action: pan-x; }
.proc--swipe .proc-card { scroll-snap-align: center; }

/* Swipe hints, phones only. */
.swipe-hint { display: none; }
@media (max-width: 899.98px) {
  #examples .swipe-hint,
  .proc--swipe .proc__hint {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s1);
    margin: var(--s3) 0 0;
    font-family: var(--font-text);
    font-size: var(--t-cite);
    font-weight: 500;
    line-height: 1.4;
    color: var(--ink-soft);
    transition: opacity var(--dur) var(--ease-out);
  }
  .swipe-hint .icon { width: 0.875rem; height: 0.875rem; }
  .swipe-hint.is-used { opacity: 0; }
}
@keyframes swipe-drift { 0%, 100% { transform: none; } 50% { transform: translateX(4px); } }
@keyframes swipe-nudge { 0%, 100% { transform: none; } 45% { transform: translateX(-28px); } }
@media (max-width: 899.98px) and (prefers-reduced-motion: no-preference) {
  .swipe-hint:not(.is-used) .icon { animation: swipe-drift 1.6s var(--ease-out) infinite; }
  .is-nudging { animation: swipe-nudge 1s var(--ease-out) 1; }
}

/* Examples (phones): dots and the swipe hint sit above the cards, where
   they are seen before the cards are, not under a long card. */
.eg-nav { display: none; }
@media (max-width: 899.98px) {
  .eg-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: var(--sp-head);
  }
  .eg-nav .eg-dots { margin: 0; justify-content: flex-start; }
  #examples .eg-nav .swipe-hint { margin: 0; }
  #examples .eg-list { margin-top: var(--s4); }
}
