/* ==========================================================================
   Post Mundane — v2.1
   The site is a drawing set. Grey is what exists, blue is what we install,
   orange is the people it is installed for.
   ========================================================================== */

/* --- tokens ------------------------------------------------------------- */
/* The light palette is defined on bare :root so it applies in the un-stamped
   "system" state. Dark redefines TOKENS ONLY, never component colours. */
:root {
  --paper:      #ffffff;
  --sheet:      #f3f4f6;
  --sheet-deep: #e9ebef;
  --ink:        #0d1117;
  --graphite:   #5b6472;   /* cool-biased neutral, not a default mid-grey */
  --faint:      #8a94a3;
  --hair:       #dcdfe5;
  --hair-soft:  #ebedf1;

  --blue:       #0b3df5;   /* the intervention */
  --blue-deep:  #0730c4;
  --blue-wash:  #e7ecfe;
  --orange:     #ff4d1c;   /* the people */
  --orange-wash:#ffe9e2;

  --on-blue:    #ffffff;
  --on-orange:  #1a0700;

  /* the model: one sun, upper-left. roof brightest, right wall darkest */
  --m-top:      #f6f7f9;
  --m-left:     #e0e3e8;
  --m-right:    #c3c8d1;
  --m-shadow:   rgba(20, 24, 32, .14);
  --m-shadow-solid: #7a818c;
  --m-edge:     rgba(70, 78, 92, .28);
  --m-ground:   #f7f8fa;

  /* type */
  --display: "Archivo", ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --serif:   "Instrument Serif", ui-serif, Georgia, "Times New Roman", serif;
  /* The spec voice is the display sans, tracked and uppercased — the
     drawing-set label without the typewriter. */
  --spec:    var(--display);

  /* measure + rhythm */
  --gutter: clamp(20px, 4.5vw, 64px);
  --rule: 1px;
  --sheet-pad: clamp(56px, 8vw, 128px);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:      #0b0d11;
    --sheet:      #12151b;
    --sheet-deep: #191d25;
    --ink:        #eef1f5;
    --graphite:   #98a1b0;
    --faint:      #6c7686;
    --hair:       #262b34;
    --hair-soft:  #1b1f27;

    --blue:       #4b73ff;
    --blue-deep:  #6b8bff;
    --blue-wash:  #131b3a;
    --orange:     #ff6b42;
    --orange-wash:#2a1109;

    --on-blue:    #060a18;
    --on-orange:  #180500;

    --m-top:      #323947;
    --m-left:     #242a34;
    --m-right:    #181c24;
    --m-shadow:   rgba(0, 0, 0, .55);
    --m-shadow-solid: #06080b;
    --m-edge:     rgba(160, 170, 190, .22);
    --m-ground:   #14181f;
  }
}

:root[data-theme="dark"] {
  --paper:      #0b0d11;
  --sheet:      #12151b;
  --sheet-deep: #191d25;
  --ink:        #eef1f5;
  --graphite:   #98a1b0;
  --faint:      #6c7686;
  --hair:       #262b34;
  --hair-soft:  #1b1f27;

  --blue:       #4b73ff;
  --blue-deep:  #6b8bff;
  --blue-wash:  #131b3a;
  --orange:     #ff6b42;
  --orange-wash:#2a1109;

  --on-blue:    #060a18;
  --on-orange:  #180500;

  --m-top:      #323947;
  --m-left:     #242a34;
  --m-right:    #181c24;
  --m-shadow:   rgba(0, 0, 0, .55);
  --m-shadow-solid: #06080b;
  --m-edge:     rgba(160, 170, 190, .22);
  --m-ground:   #14181f;
}

/* --- base --------------------------------------------------------------- */
* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  /* The viewer paints its own ground behind the page — this must be explicit. */
  background: var(--paper);
  color: var(--ink);
  font-family: var(--display);
  font-size: 16px;
  line-height: 1.55;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

a { color: inherit; }

::selection { background: var(--blue); color: var(--on-blue); }

:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 1px;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--ink); color: var(--paper);
  padding: 12px 18px; font-family: var(--spec); font-size: 12px;
}
.skip:focus { left: 8px; top: 8px; }

/* --- typographic scale -------------------------------------------------- */
.t-display {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(2.15rem, 4.5vw, 3.85rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  text-wrap: pretty;
  margin: 0;
}
.t-h2 {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(1.9rem, 3.6vw, 3rem);
  line-height: 1.04;
  letter-spacing: -0.028em;
  text-wrap: balance;
  margin: 0;
}
.t-h3 {
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(1.05rem, 1.5vw, 1.3rem);
  line-height: 1.2;
  letter-spacing: -0.018em;
  text-wrap: balance;
  margin: 0;
}
/* The one editorial signature: a single italic serif word per headline. */
.em {
  font-family: var(--serif);
  font-weight: 400;
  font-style: italic;
  letter-spacing: -0.01em;
  /* Instrument Serif runs small against Archivo at the same size. */
  font-size: 1.1em;
  line-height: 0.9;
}
.lede {
  font-size: clamp(1.02rem, 1.35vw, 1.2rem);
  line-height: 1.5;
  color: var(--graphite);
  max-width: 46ch;
  margin: 0;
  text-wrap: pretty;
}
.body { max-width: 62ch; color: var(--graphite); margin: 0; text-wrap: pretty; }
.body strong { color: var(--ink); font-weight: 600; }

/* Spec label — the drawing-set voice. */
.spec {
  font-family: var(--spec);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0;
}
.spec--ink { color: var(--ink); }
.spec--blue { color: var(--blue); }

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

/* --- layout ------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: 1560px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* A sheet is a section of the drawing set: hairline top, title block, content. */
.sheet {
  border-top: var(--rule) solid var(--hair);
  padding-block: var(--sheet-pad);
  position: relative;
}
.sheet--flush { padding-block: 0; }
.sheet--tint { background: var(--sheet); }

/* Title block: sheet number + name, mono, in a left rail where there is room. */
.titleblock {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 10px;
  margin-bottom: clamp(32px, 4vw, 56px);
}
.titleblock__row {
  display: flex; align-items: baseline; gap: 14px;
  padding-bottom: 10px;
  border-bottom: var(--rule) solid var(--hair);
}
.titleblock__no { color: var(--blue); }
.titleblock__name { color: var(--ink); }
.titleblock__scale { margin-left: auto; }

/* --- header ------------------------------------------------------------- */
.top {
  position: sticky; top: 0; z-index: 40;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: var(--rule) solid var(--hair);
}
.top__in {
  display: flex; align-items: center; gap: 24px;
  height: 62px;
}
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  text-decoration: none; color: var(--ink);
  font-weight: 600; font-size: 14px; letter-spacing: -0.01em;
  white-space: nowrap;
}
.brand__mark {
  width: 15px; height: 15px; flex: none;
  border: 1.5px solid var(--blue);
  position: relative;
}
.brand__mark::after {
  content: ""; position: absolute; inset: 3px 3px auto auto;
  width: 5px; height: 5px; background: var(--orange);
}
.nav { display: flex; gap: 4px; margin-left: auto; }
.nav a {
  font-family: var(--spec); font-size: 11.5px; letter-spacing: 0.08em;
  text-transform: uppercase; text-decoration: none; color: var(--graphite);
  padding: 7px 11px; border-radius: 2px;
  transition: color .16s ease, background-color .16s ease;
}
.nav a:hover { color: var(--ink); background: var(--sheet); }
.nav a[aria-current="page"] { color: var(--ink); }
.nav a[aria-current="page"]::after {
  content: ""; display: block; height: 1.5px; background: var(--blue); margin-top: 5px;
}
/* Below 860 the nav became a second row rather than disappearing — hiding it
   with no replacement left phones with no way to reach four of five pages. */
@media (max-width: 860px) {
  .top__in { height: auto; flex-wrap: wrap; gap: 0; padding-block: 11px 0; }
  .brand { width: 100%; }
  .nav {
    width: 100%; margin-left: 0; gap: 0;
    overflow-x: auto; scrollbar-width: none;
    padding-bottom: 2px;
  }
  .nav::-webkit-scrollbar { display: none; }
  .nav a { padding: 8px 12px 8px 0; white-space: nowrap; }
  .nav a[aria-current="page"]::after { margin-top: 3px; }
}

/* --- buttons ------------------------------------------------------------ */
/* Declare background-color, never the `background` shorthand — transitioning
   the shorthand makes every computed-style read land mid-flight. */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--spec); font-size: 12px; letter-spacing: 0.07em;
  text-transform: uppercase; text-decoration: none;
  padding: 14px 20px; border: var(--rule) solid transparent;
  background-color: var(--ink); color: var(--paper);
  cursor: pointer; border-radius: 2px;
  transition: background-color .18s ease, color .18s ease, border-color .18s ease, transform .18s ease;
}
.btn:hover { background-color: var(--blue); transform: translateY(-1px); }
.btn__arrow { transition: transform .18s ease; }
.btn:hover .btn__arrow { transform: translateX(4px); }

.btn--ghost {
  background-color: transparent; color: var(--ink); border-color: var(--hair);
}
.btn--ghost:hover {
  background-color: transparent; color: var(--blue); border-color: var(--blue);
}

/* --- hero --------------------------------------------------------------- */
.hero { border-top: 0; padding-block: clamp(48px, 6vw, 92px) clamp(40px, 5vw, 72px); }
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
  gap: clamp(32px, 5vw, 72px);
  align-items: center;
}
@media (max-width: 1000px) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); gap: 40px; }
}
.hero__copy { display: flex; flex-direction: column; gap: 26px; align-items: flex-start; }
.hero__title { max-width: 21ch; }
.hero__actions { display: flex; flex-wrap: wrap; gap: 10px; }
/* the open letter: a plain text link, dotted underline, under the buttons */
.hero__letter {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 10px;
  font-size: .95rem; font-weight: 500; letter-spacing: -0.01em;
  color: var(--ink); text-decoration: none;
  transition: color .16s ease;
}
/* the underline sits under the words only, not the arrow */
.hero__letter-text {
  text-decoration: underline dotted; text-decoration-thickness: 1px; text-underline-offset: 6px;
  text-decoration-color: color-mix(in srgb, var(--ink) 55%, transparent);
  transition: text-decoration-color .16s ease;
}
.hero__letter-arrow { display: inline-block; transition: transform .16s ease; }
.hero__letter:hover, .hero__letter:focus-visible { color: var(--orange); }
.hero__letter:hover .hero__letter-text, .hero__letter:focus-visible .hero__letter-text { text-decoration-color: var(--orange); }
.hero__letter:hover .hero__letter-arrow { transform: translateX(4px); }

/* The drawing sits in a plate with a faint construction grid. */
.plate {
  position: relative;
  background: var(--sheet);
  border: var(--rule) solid var(--hair);
  border-radius: 2px;
  overflow: hidden;
}
.plate::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, var(--hair-soft) 1px, transparent 1px),
    linear-gradient(to bottom, var(--hair-soft) 1px, transparent 1px);
  background-size: 32px 32px;
  opacity: .9;
}
.plate__canvas { position: relative; display: block; width: 100%; height: auto; }

/* --- hero on paper --------------------------------------------------------- */
/* The hero is light whatever the site's theme: a soft white ground, the
   town in its white-model tones, and only the blue in colour. Everything
   inside is on tokens, so this is the light palette scoped to the section,
   with a softer paper than pure white. Drop the class to go back. */
.hero--paper {
  --paper:      #f5f6f8;
  --sheet:      #eceef2;
  --sheet-deep: #e2e5ea;
  --ink:        #0d1117;
  --graphite:   #5b6472;
  --faint:      #8a94a3;
  --hair:       #d6dae1;
  --hair-soft:  #e4e7ec;
  --blue:       #0b3df5;
  --blue-deep:  #0730c4;
  --orange:     #ff4d1c;
  --on-blue:    #ffffff;
  --on-orange:  #1a0700;
  --m-top:      #f9fafb;
  --m-left:     #dfe2e7;
  --m-right:    #c3c8d1;
  --m-shadow:   rgba(20, 24, 32, .14);
  --m-shadow-solid: #7a818c;
  --m-edge:     rgba(70, 78, 92, .28);
  --m-ground:   #f2f3f6;
  background: var(--paper);
  color: var(--ink);
}

/* The nav goes on paper while the paper hero is under it, and back to the
   page's own theme once the reader has scrolled past. Toggled by town.js. */
.top--paper {
  --paper: #f5f6f8; --sheet: #eceef2; --ink: #0d1117; --graphite: #5b6472;
  --hair: #d6dae1; --blue: #0b3df5; --orange: #ff4d1c;
  /* opaque: what sits behind the bar at the top of the page is the dark
     body, not the paper, and 88% of paper over that is mud */
  background: var(--paper);
  transition: background-color .35s ease, border-color .35s ease;
}

/* --- hero with the town under it ------------------------------------------ */
.hero--town { position: relative; overflow: hidden; min-height: min(86vh, 860px); display: grid; align-items: center; }
/* The grid spans the whole section, empty right column included, and the
   mask on the art makes it a stacking context of its own — so the grid
   must not take the pointer, or nothing on the town is clickable. */
.hero--town .hero__grid { grid-template-columns: minmax(0, 46%) minmax(0, 1fr); position: relative; z-index: 1; pointer-events: none; }
.hero--town .hero__copy { pointer-events: auto; }
/* The right part of the section, edge to edge. A mask dissolves the town
   into the page on three sides; the fourth runs off the viewport. */
.hero__art {
  position: absolute; top: 0; bottom: 0; left: 44%; right: 0;
  pointer-events: none;
  --fade: linear-gradient(to right, transparent 0%, #000 26%, #000 100%),
          linear-gradient(to bottom, transparent 0%, #000 14%, #000 84%, transparent 100%);
  -webkit-mask-image: var(--fade); mask-image: var(--fade);
  -webkit-mask-composite: source-in; mask-composite: intersect;
}
.hero__art .town { pointer-events: auto; }
/* atmosphere: the far side of the town pales into the page */
.hero__art::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 1;
  background: linear-gradient(to bottom, var(--paper) 0%, transparent 52%);
  opacity: .75;
}
.town--fill { width: 100%; height: 100%; display: block; }
/* Stacked: the copy reads first, the town follows it. */
@media (max-width: 1000px) {
  .hero--town { min-height: 0; display: flex; flex-direction: column; align-items: stretch; }
  .hero--town .hero__grid { grid-template-columns: minmax(0, 1fr); order: 1; }
  .hero__art {
    order: 2;
    position: relative; left: auto; right: auto; inset: auto; height: auto;
    aspect-ratio: 16 / 9; margin: 28px 0 0;
    --fade: linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%),
            linear-gradient(to bottom, transparent 0%, #000 12%, #000 88%, transparent 100%);
  }
}
@media (max-width: 600px) { .hero__art { aspect-ratio: 4 / 3; } }

/* --- the town ------------------------------------------------------------ */
/* All colour is here, on tokens, so the drawing re-themes with the page.
   Strokes are non-scaling: a hairline is a hairline at every plate width. */
.plate__svg { position: relative; display: block; width: 100%; height: auto; overflow: visible; }
.town path, .town circle { vector-effect: non-scaling-stroke; }
.town__plane { fill: var(--m-ground); stroke: none; }
.town__ground path { fill: none; stroke: var(--hair); stroke-width: 1; stroke-opacity: .9; }
/* Faces are opaque, like a card model — a building in front hides the one
   behind. Lit from above-left: roof lightest, right wall darkest. */
.town .f { stroke: var(--m-edge); stroke-width: .75; stroke-opacity: 1; stroke-linejoin: round;
           transition: fill .55s ease, fill-opacity .55s ease, stroke .55s ease, stroke-opacity .55s ease; }
.town .f-t { fill: url(#town-top); }
.town .f-l { fill: url(#town-wl); }
.town .f-r { fill: url(#town-wr); }
.town .f-b { fill: var(--m-left); }
/* gradient stops: base of the wall sits in occlusion, top meets the sky */
.town .g-l0 { stop-color: color-mix(in srgb, var(--m-left) 78%, var(--m-shadow-solid)); }
.town .g-l1 { stop-color: var(--m-left); }
.town .g-r0 { stop-color: color-mix(in srgb, var(--m-right) 72%, var(--m-shadow-solid)); }
.town .g-r1 { stop-color: var(--m-right); }
.town .g-t0 { stop-color: color-mix(in srgb, var(--m-top) 92%, #fff); }
.town .g-t1 { stop-color: var(--m-top); }
.town .b--plot .f-t { fill: var(--m-right); fill-opacity: .5; stroke-opacity: .25; }
/* the detail: faint in grey, so the town has grain without noise */
.town .dt { fill: none; stroke: var(--m-edge); stroke-width: .6; stroke-opacity: .55; stroke-linecap: round;
            transition: stroke .55s ease, stroke-opacity .55s ease; }
.town__shadow path { fill: var(--m-shadow); stroke: none; }
.town .b--roof .f, .town .b--cap .f { stroke-opacity: .26; }

/* over the render: nothing is drawn until it turns blue, and the blue is a
   wash the model shows through — the windows stay visible under it */
.town--image .f { fill: transparent; stroke: none; }
.town--image .dt { stroke: transparent; }
.town--image .b--pick.is-filled .f { stroke: none; }
.town--image .b--pick.is-filled .f-t { fill: color-mix(in srgb, var(--blue) 78%, #fff); fill-opacity: .9; }
.town--image .b--pick.is-filled .f-l { fill: var(--blue); fill-opacity: .82; }
.town--image .b--pick.is-filled .f-r { fill: color-mix(in srgb, var(--blue) 80%, #000); fill-opacity: .86; }
.town--image .b--pick.is-filled .dt { stroke: #fff; stroke-opacity: .5; stroke-width: .9; }
.town--image .ol { stroke-width: 2; }
/* masked picks: one blue, multiplied over the render, so roof, walls and
   the shadow in every window come from the model itself */
.town--image .b--masked .f { fill: var(--blue); fill-opacity: 0; mix-blend-mode: multiply;
                             transition: fill-opacity .55s ease; }
.town--image .b--masked.is-filled .f { fill-opacity: .92; }
.town--image .b--masked.is-filled .dt { stroke: #fff; stroke-opacity: .45; }

/* the intervention: outline drawn, then the fill completes */
.town .b--pick { cursor: pointer; outline: none; }
.town .ol { fill: none; stroke: var(--blue); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round;
            opacity: 0; transition: opacity .2s ease, stroke-dashoffset 1.4s cubic-bezier(.4, 0, .2, 1); }
.town .b--pick.is-drawing .ol { opacity: 1; stroke-dashoffset: 0 !important; }
.town .b--pick.is-filled .f { stroke: var(--blue); stroke-opacity: .95; }
/* same sun on the blue: roof lit, right wall in its own shade */
.town .b--pick.is-filled .f-t { fill: color-mix(in srgb, var(--blue) 66%, #fff); }
.town .b--pick.is-filled .f-l { fill: var(--blue); }
.town .b--pick.is-filled .f-r { fill: color-mix(in srgb, var(--blue) 76%, #000); }
/* on the blue, the detail stays: pale lines over the fill, like the wireframe */
.town .b--pick.is-filled .dt { stroke: #fff; stroke-opacity: .55; }
.town .b--pick:hover .f, .town .b--pick:focus-visible .f { stroke: var(--blue); stroke-opacity: .9; }
/* clearing: everything relaxes together, and the line is not re-drawn backwards */
.town.is-clearing .ol { transition: opacity .8s ease; }
.town.is-clearing .ol { stroke-dashoffset: 0 !important; }

/* the town plots in, far to near */
.town .b { opacity: 0; }
.town.is-in .b { animation: town-plot .5s ease both; animation-delay: calc(var(--i) * 2.2ms); }
.town.no-plot .b, .no-js .town .b { opacity: 1; animation: none; }
@keyframes town-plot { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .town .b { opacity: 1; animation: none !important; }
}

/* the station button, standing on the roof: a small square .btn on a leader */
.town-btn {
  position: absolute; z-index: 2; transform: translate(-50%, -100%);
  display: flex; flex-direction: column; align-items: center;
  text-decoration: none; pointer-events: none;
  opacity: 0; transition: opacity .5s ease .25s;
}
.town-btn.is-on { opacity: 1; }
.town-btn__leader { width: 1px; height: 26px; background: var(--blue); }

.plate__tag {
  position: absolute; z-index: 2;
  font-family: var(--spec); font-size: 10.5px; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--faint);
}
.plate__tag--tl { top: 12px; left: 14px; }
.plate__tag--br { bottom: 12px; right: 14px; }

/* Legend that names the drawing's colour code. This is the FRM-6 argument. */
.legend { display: flex; flex-wrap: wrap; gap: 18px 26px; }
.legend__item { display: flex; align-items: center; gap: 8px; }
.legend__key { width: 11px; height: 11px; flex: none; border: 1.5px solid var(--faint); }
.legend__key--blue { border-color: var(--blue); background: color-mix(in srgb, var(--blue) 26%, transparent); }
.legend__key--orange { border-color: var(--orange); background: color-mix(in srgb, var(--orange) 34%, transparent); }
.legend__text { font-family: var(--spec); font-size: 11px; letter-spacing: 0.06em; color: var(--graphite); }
.legend__text b { color: var(--ink); font-weight: 500; }

/* --- the stat ----------------------------------------------------------- */
.stat__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(32px, 5vw, 80px); align-items: start;
}
@media (max-width: 900px) { .stat__grid { grid-template-columns: minmax(0, 1fr); gap: 32px; } }
.stat__figure {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(4.5rem, 13vw, 10rem); line-height: 0.82;
  letter-spacing: -0.05em; color: var(--blue);
  display: block;
}
.stat__figure sup { font-size: 0.32em; vertical-align: super; letter-spacing: 0; }
.stat__cite {
  margin-top: 22px; padding-top: 14px;
  border-top: var(--rule) solid var(--hair);
}
.stat__copy { display: flex; flex-direction: column; gap: 20px; }

/* Bar that shows 95 / 5 without a chart library. */
.bar { display: flex; height: 16px; border: var(--rule) solid var(--hair); border-radius: 2px; overflow: hidden; }
.bar__fail { flex: 95; background-image:
  repeating-linear-gradient(135deg, var(--faint) 0 1px, transparent 1px 6px); }
.bar__win { flex: 5; background-color: var(--blue); }
.bar__key { display: flex; margin-top: 9px; }
.bar__key span { flex: 95; }
.bar__key span + span { flex: 5; text-align: right; color: var(--blue); }

/* --- exploded layers (the leverage diagram) ------------------------------ */
.layers__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(28px, 4vw, 64px); align-items: center;
}
@media (max-width: 980px) { .layers__grid { grid-template-columns: minmax(0, 1fr); } }
.layers__list { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
.layers__item {
  display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 16px;
  padding: 18px 0; border-bottom: var(--rule) solid var(--hair);
  cursor: pointer; background: none; border-inline: 0; border-top: 0;
  text-align: left; width: 100%; font: inherit; color: inherit;
  transition: opacity .2s ease;
}
.layers__item:first-child { border-top: var(--rule) solid var(--hair); }
.layers__no { font-family: var(--spec); font-size: 11px; color: var(--faint); padding-top: 3px; letter-spacing: .06em; }
.layers__item[aria-selected="true"] .layers__no { color: var(--blue); }
.layers__item[aria-selected="true"] .layers__name { color: var(--ink); }
.layers__name { font-weight: 600; font-size: 1rem; letter-spacing: -0.015em; color: var(--graphite); transition: color .2s ease; }
.layers__note {
  font-size: 0.9rem; color: var(--graphite); margin: 6px 0 0;
  max-width: 48ch; display: none;
}
.layers__item[aria-selected="true"] .layers__note { display: block; }

/* --- studio index (District 9 device) ------------------------------------ */
.index {
  background-color: var(--blue);
  color: var(--on-blue);
  border-radius: 2px;
  overflow: hidden;
}
.index__head {
  display: flex; align-items: baseline; gap: 16px; flex-wrap: wrap;
  padding: clamp(24px, 3vw, 38px) clamp(20px, 3vw, 36px);
}
.index__title {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(1.7rem, 3.4vw, 2.9rem); line-height: 1;
  letter-spacing: -0.03em; margin: 0; color: var(--on-blue);
}
.index__sheet { margin-left: auto; font-family: var(--spec); font-size: 12px; letter-spacing: .12em; opacity: .72; }
.index__rows { display: grid; }
/* Each line IS its thesis: a <details> whose summary is the row and whose
   panel is the argument. The grid that used to sit on the row moves to the
   summary, so the row keeps its exact rhythm whether it is open or shut. */
.index__row {
  border-top: var(--rule) solid color-mix(in srgb, var(--on-blue) 22%, transparent);
  color: var(--on-blue);
}
/* Two shapes share this row. On /approach/ it is a <details> and the layout
   lives on .index__sum; on the home page's studio index it is still a plain
   link that carried its layout on the row itself. Moving the grid to the
   summary took the link's grid, padding and underline suppression with it,
   which left sheet 06 as a stack of underlined text on blue. This gives the
   link variant its own layout back without touching the disclosure. */
a.index__row {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr) auto;
  gap: 20px; align-items: baseline;
  padding: 15px clamp(20px, 3vw, 36px);
  text-decoration: none;
  transition: background-color .16s ease;
}
a.index__row:hover { background-color: color-mix(in srgb, var(--on-blue) 10%, transparent); }
.index__sum {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr) auto 15px;
  gap: 20px; align-items: baseline;
  padding: 15px clamp(20px, 3vw, 36px);
  cursor: pointer; list-style: none;
  transition: background-color .16s ease;
}
.index__sum::-webkit-details-marker { display: none; }
.index__sum:hover { background-color: color-mix(in srgb, var(--on-blue) 10%, transparent); }
.index__sum:focus-visible { outline: 2px solid var(--on-blue); outline-offset: -3px; }
.index__row[open] .index__sum { background-color: color-mix(in srgb, var(--on-blue) 10%, transparent); }

/* the arrow: a chevron that points down, and up once the panel is open */
.index__mark {
  position: relative; align-self: center; width: 15px; height: 15px;
}
.index__mark::before {
  content: ""; position: absolute; left: 3px; top: 4px; width: 7px; height: 7px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg); transform-origin: 60% 60%;
  transition: transform .22s cubic-bezier(.22,.61,.36,1);
}
.index__row[open] .index__mark::before { transform: rotate(225deg); }

.index__panel {
  display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  column-gap: clamp(24px, 4vw, 64px); row-gap: 14px;
  padding: 2px clamp(20px, 3vw, 36px) 26px;
  margin-left: 188px;
}
@media (max-width: 1100px) { .index__panel { grid-template-columns: minmax(0, 1fr); } }
.index__arg { margin: 0; font-size: .95rem; line-height: 1.55; }
.index__anchor {
  margin: 0; font-family: var(--spec); font-size: 11.5px; line-height: 1.7;
  letter-spacing: .02em; opacity: .82;
  border-left: var(--rule) solid color-mix(in srgb, var(--on-blue) 40%, transparent);
  padding-left: 14px;
}
.index__anchor b { display: block; font-size: 10px; letter-spacing: .12em; text-transform: uppercase; opacity: .8; margin-bottom: 4px; }
.index__cat {
  font-family: var(--spec); font-size: 10.5px; letter-spacing: .13em;
  text-transform: uppercase; opacity: .66;
}
.index__name { font-weight: 500; font-size: 1.02rem; letter-spacing: -0.015em; }
.index__desc { display: block; font-weight: 400; font-size: .86rem; opacity: .74; margin-top: 3px; max-width: 54ch; }
.index__status {
  font-family: var(--spec); font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  border: var(--rule) solid color-mix(in srgb, var(--on-blue) 40%, transparent);
  padding: 4px 8px; white-space: nowrap; opacity: .9;
}
.index__status--live { background-color: var(--orange); color: var(--on-orange); border-color: var(--orange); opacity: 1; }
@media (max-width: 780px) {
  .index__sum { grid-template-columns: minmax(0, 1fr) auto 15px; }
  a.index__row { grid-template-columns: minmax(0, 1fr) auto; }
  .index__cat { grid-column: 1 / -1; }
  .index__panel { margin-left: 0; }
}

/* --- three-up (Hylix spec cards) ---------------------------------------- */
.three { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(20px, 3vw, 48px); }
@media (max-width: 880px) { .three { grid-template-columns: minmax(0, 1fr); gap: 28px; } }
.three__col { display: flex; flex-direction: column; gap: 10px; padding-top: 18px; border-top: 2px solid var(--ink); }
.three__col p { margin: 0; font-size: .92rem; color: var(--graphite); }

/* --- form --------------------------------------------------------------- */
.form { display: grid; gap: 16px; max-width: 560px; }
.form .btn { justify-self: start; }
.field { display: grid; gap: 7px; }
.field label { font-family: var(--spec); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--graphite); }
.field input, .field textarea, .field select {
  font: inherit; font-size: .95rem;
  padding: 12px 14px; border-radius: 2px;
  border: var(--rule) solid var(--hair);
  background-color: var(--paper); color: var(--ink);
  transition: border-color .16s ease;
}
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--blue); }
.field textarea { min-height: 132px; resize: vertical; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form__note { font-size: .82rem; color: var(--faint); margin: 0; }
.form__status { font-family: var(--spec); font-size: 12px; margin: 0; min-height: 1.4em; }
.form__status[data-state="ok"]  { color: var(--blue); }
.form__status[data-state="err"] { color: var(--orange); }

/* --- footer ------------------------------------------------------------- */
.foot { border-top: 2px solid var(--ink); padding-block: clamp(40px, 5vw, 72px) 32px; }
.foot__grid {
  display: grid; grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 48px);
}
@media (max-width: 880px) { .foot__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .foot__grid { grid-template-columns: minmax(0, 1fr); } }
.foot__col { display: flex; flex-direction: column; gap: 9px; align-items: flex-start; }
.foot__col a { font-size: .92rem; color: var(--graphite); text-decoration: none; transition: color .16s ease; }
.foot__col a:hover { color: var(--blue); }
.foot__base {
  margin-top: clamp(36px, 5vw, 64px); padding-top: 18px;
  border-top: var(--rule) solid var(--hair);
  display: flex; flex-wrap: wrap; gap: 12px 28px; align-items: baseline;
}
.foot__base .spec:last-child { margin-left: auto; }

/* --- reveal ------------------------------------------------------------- */
/* Scroll-linked, so it only moves while the reader moves. Kept under
   reduced-motion; only autonomous animation is dropped. */
.rise { opacity: 0; transform: translateY(14px); transition: opacity .6s ease, transform .6s cubic-bezier(.22,.61,.36,1); }
.rise.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .rise { transition-duration: .01ms; }
  .btn:hover { transform: none; }
}
.no-js .rise { opacity: 1; transform: none; }

/* --- page header -------------------------------------------------------- */
.phead { padding-block: clamp(34px, 4.5vw, 64px) clamp(28px, 3.5vw, 48px); }
.phead__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 64px); align-items: end;
}
@media (max-width: 900px) { .phead__grid { grid-template-columns: minmax(0, 1fr); gap: 28px; } }
.phead__copy { display: flex; flex-direction: column; gap: 18px; align-items: flex-start; }

/* --- spec card (Hylix title-block treatment) ---------------------------- */
.spec-card {
  border-top: 2px solid var(--ink);
  padding-top: 20px;
  display: grid; gap: 14px;
  scroll-margin-top: 84px;
}
.spec-card--build { border-top-color: var(--hair); }
.spec-card__head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.spec-card__status {
  margin-left: auto;
  font-family: var(--spec); font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  border: var(--rule) solid var(--hair); padding: 4px 8px; color: var(--graphite);
}
.spec-card__status--live {
  background-color: var(--orange); border-color: var(--orange); color: var(--on-orange);
}
.spec-card__body { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: clamp(20px, 3vw, 48px); }
@media (max-width: 820px) { .spec-card__body { grid-template-columns: minmax(0, 1fr); gap: 18px; } }
.spec-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.spec-list li {
  display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 12px;
  padding: 9px 0; border-bottom: var(--rule) solid var(--hair-soft);
  font-size: .88rem; color: var(--graphite);
}
.spec-list li::before {
  content: attr(data-state); font-family: var(--spec); font-size: 10px;
  letter-spacing: .08em; color: var(--faint); padding-top: 3px; min-width: 62px;
}
.spec-list li[data-state="DONE"]::before { color: var(--blue); }

.stack-gap { display: grid; gap: clamp(40px, 5vw, 72px); }

/* --- report list -------------------------------------------------------- */
.reports { display: grid; max-width: 1020px; }
.report {
  display: grid; grid-template-columns: 148px minmax(0, 1fr);
  gap: clamp(16px, 3vw, 40px);
  padding: 26px 0; border-top: var(--rule) solid var(--hair);
}
.reports .report:last-child { border-bottom: var(--rule) solid var(--hair); }
@media (max-width: 720px) { .report { grid-template-columns: minmax(0, 1fr); gap: 10px; } }
.report__body { display: grid; gap: 8px; }

/* --- sheet 01: the counted stat (parallel session) ----------------------- */
/* Number + subject read as one sentence; bar drains from full blue to 5%.
   The blue stays a highlight over the hatched "what exists" track — it is
   the survivor, not a material. */
.stat--count .stat__figure { display: inline-flex; align-items: flex-start; }
.stat--count .stat__n { display: inline-block; }
.stat__lead { margin-top: 20px; max-width: 22ch; font-size: clamp(1.5rem, 2.6vw, 2.15rem); }
.stat--count .bar { margin-top: 30px; }
.bar--drain { position: relative; }
.bar--drain .bar__fail { position: absolute; inset: 0; flex: none; }
.bar--drain .bar__win {
  position: absolute; top: 0; right: 0; bottom: 0; flex: none;
  width: var(--win, 5%);
}
.bar__key-win { transition: opacity .6s ease; font-weight: 600; font-size: 13px; color: var(--blue); }
.stat--count.is-armed .bar__key-win { opacity: 0; }
.stat--count.is-done  .bar__key-win { opacity: 1; }
.stat--count .stat__copy { padding-top: clamp(0px, 4vw, 44px); }

/* --- buttons: split pill (parallel session) ------------------------------ */
/* Ported from the 21st.dev "Start a Project" pattern — a text segment and
   a separate arrow cap with a seam between, orange on hover, the arrow
   leaving right as a second arrives from the left — but SQUARE: Will
   squared it off on 2026-08-25. Overrides .btn above. */
.btn {
  /* On tokens, not #fff: the pill is white on the dark page and ink on
     the light one — a white pill on white paper was invisible. */
  --btn-bg: var(--ink); --btn-fg: var(--paper); --cap: 46px;
  gap: 0; padding: 0 0 0 22px; border: 0; border-radius: 2px;   /* square, Will's call */
  line-height: var(--cap);
  background: var(--btn-bg); color: var(--btn-fg);
  font-family: var(--display); font-size: 15px; font-weight: 500;
  letter-spacing: 0; text-transform: none;
  transition: background-color .45s cubic-bezier(.22,.61,.36,1), color .45s ease;
}
.btn:hover, .btn:focus-visible {
  --btn-bg: var(--orange); --btn-fg: var(--on-orange);
  background: var(--btn-bg); transform: none;
}
.btn__arrow {
  position: relative; display: inline-block; overflow: hidden;
  width: var(--cap); height: var(--cap); margin-left: 16px;
  border-radius: 2px; background: var(--btn-bg);
  box-shadow: 0 0 0 3px var(--paper); /* the seam between pill and cap */
  color: transparent;                  /* hide the markup's own glyph */
  font-size: 18px; transform: none; transition: background-color .45s cubic-bezier(.22,.61,.36,1);
}
.btn:hover .btn__arrow { transform: none; }
.btn__arrow::before, .btn__arrow::after {
  content: "\2197"; position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--btn-fg); transition: transform .5s cubic-bezier(.22,.61,.36,1);
}
.btn__arrow::after { transform: translateX(calc(-1 * var(--cap))); }
.btn:hover .btn__arrow::before, .btn:focus-visible .btn__arrow::before { transform: translateX(var(--cap)); }
.btn:hover .btn__arrow::after,  .btn:focus-visible .btn__arrow::after  { transform: translateX(0); }

/* Ghost: same shape, hairline, no cap. */
.btn--ghost {
  --btn-bg: transparent; --btn-fg: var(--ink);
  padding: 0 22px; border: var(--rule) solid var(--hair);
}
.btn--ghost:hover, .btn--ghost:focus-visible {
  --btn-bg: transparent; --btn-fg: var(--orange); background: transparent; border-color: var(--orange);
}

/* Small: the roof buttons on the town. Mono label, tighter cap, and the
   hover rides on the enclosing link so the leader counts as the button. */
.btn--xs {
  --cap: 28px; padding-left: 11px;
  font-family: var(--spec); font-size: 10.5px; font-weight: 500;
  letter-spacing: .12em; text-transform: uppercase;
}
.btn--xs .btn__arrow { margin-left: 9px; font-size: 13px; box-shadow: 0 0 0 2px var(--paper); }
/* .btn has no right padding — the arrow cap used to BE the right edge. The
   roof labels have no cap any more, so the last letter sat on the box edge
   and read as clipped. 10px rather than 11 because the uppercase tracking
   already leaves a gap after the final letter. */
.town-btn .btn--xs { padding-right: 10px; }
/* hover/focus from the link wrapping it */
/* the roof labels are not controls any more: no hover colour, no arrow slide */

/* --- page flow: one paper, one rail (parallel session) ------------------- */
/* Framer/Figma both do "continuous" the same way: one surface, no rules or
   tint bands between sections, one heading pattern repeated, and something
   that persists while the page moves. The sheet/titleblock grammar stays as
   a margin note; it stops being the section structure. */
.sheet { border-top: 0; padding-block: clamp(20px, 3vw, 40px) clamp(72px, 9vw, 144px); }
.sheet--tint { background: transparent; }
.sheet + .sheet { padding-top: 0; }
.hero + .sheet { padding-top: clamp(56px, 7vw, 104px); }
.sheet + .sheet .titleblock { padding-top: 40px; } /* room for the rail crossing above the label */
.titleblock { margin-bottom: clamp(18px, 2.4vw, 30px); }
.titleblock__row { border-bottom: 0; padding-bottom: 0; gap: 12px; }
.titleblock__name { color: var(--graphite); }
.titleblock__scale { display: none; }

#main { position: relative; }
/* The rail is a serpentine: down the left gutter, across the page at the
   start of each even sheet, down the right, back across at the next. Even
   sheets mirror their two columns so the drawing sits on the rail's side. */
.rail { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; display: none; overflow: visible; }
@media (min-width: 1100px) { .rail { display: block; } }
.rail__line { display: none; } /* nothing ahead of the reader: the line exists only where it has been drawn */
.rail__fill { fill: none; stroke: var(--blue); stroke-width: 2; stroke-linejoin: round; }
.rail__tip  { fill: var(--blue); stroke: color-mix(in srgb, var(--blue) 22%, transparent); stroke-width: 8; }
.rail__tick { fill: var(--blue); opacity: 0; transition: opacity .2s ease; }
.rail__tick.is-past { opacity: 1; }
.sheet--flip .layers__grid > :first-child, .sheet--flip .stat__grid > :first-child { order: 2; }
.sheet--flip .titleblock__row { flex-direction: row-reverse; }
.sheet--flip .titleblock__row .titleblock__no { order: 3; }
.sheet--flip .layers__grid { align-items: start; } /* heading and drawing share the same offset below the crossing */
/* 02: the heading ranges right, flush with the "02" label above it. */
.sheet--flip .layers__grid .t-h2 { text-align: right; margin-left: auto; }

/* --- card (parallel session) --------------------------------------------- */
/* After Indie UI's grid-pattern card: hairline box, a faint drawing grid
   that fades out behind the text, one small line icon. The grid is the same
   grey-is-what-exists language as the plates. */
.card {
  position: relative; overflow: hidden;
  border: var(--rule) solid var(--hair); border-radius: 2px;
  background: color-mix(in srgb, var(--ink) 3%, var(--paper));
  padding: clamp(22px, 2.6vw, 34px);
  display: flex; flex-direction: column; gap: 18px;
}
.card--grid::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image:
    linear-gradient(to right, color-mix(in srgb, var(--ink) 9%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--ink) 9%, transparent) 1px, transparent 1px);
  background-size: 28px 28px;
  -webkit-mask-image: radial-gradient(ellipse 80% 70% at 100% 0%, #000 0%, transparent 70%);
          mask-image: radial-gradient(ellipse 80% 70% at 100% 0%, #000 0%, transparent 70%);
}
.card > * { position: relative; }
.card__head { display: flex; align-items: center; gap: 10px; padding-bottom: 14px; border-bottom: var(--rule) solid var(--hair); }
.card__icon { width: 18px; height: 18px; color: var(--blue); flex: none; }
.card__label { color: var(--ink); }
.card__meta { margin-left: auto; color: var(--graphite); }
.card .body { max-width: none; }
.card__foot { display: flex; gap: 12px; padding-top: 14px; border-top: var(--rule) solid var(--hair); color: var(--graphite); }
.card__foot-r { margin-left: auto; }
.stat--count .stat__copy { padding-top: 0; }
.stat--count .stat__cite { display: none; } /* the citation moved into the card */

/* 02: the drawing out of its box — no plate, no grid, no tags — centred on the left. */
.sheet--flip .plate { background: transparent; border: 0; overflow: visible; }
.sheet--flip .plate::before { display: none; }
.sheet--flip .plate__tag { display: none; }
.sheet--flip .layers__grid { align-items: center; }

/* --- 05 the pathway (parallel session) ----------------------------------- */
/* A time chart in the drawing-set grammar: five bands across, three lanes
   down. The progress line rises band by band; at Scope it can also stop. */
.path { position: relative; border-top: var(--rule) solid var(--hair); }
.path__line { position: absolute; left: 0; right: 0; top: 0; width: 100%; height: 120px; pointer-events: none; overflow: visible; }
.path__track { fill: none; stroke: var(--hair); stroke-width: 1.5; }
.path__rise { fill: none; stroke: var(--blue); stroke-width: 2; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.path__stop { fill: none; stroke: var(--faint); stroke-width: 1.5; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; }
.path__gate { fill: var(--paper); stroke: var(--blue); stroke-width: 2; vector-effect: non-scaling-stroke; }
.path__head, .path__lane { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); }
.path__head { padding-top: 120px; }
.path__band { padding: 14px 16px 18px 0; border-top: var(--rule) solid var(--hair); display: flex; flex-direction: column; gap: 6px; }
.path__band + .path__band { padding-left: 16px; border-left: var(--rule) solid var(--hair); }
.path__band--gate { border-top-color: var(--blue); }
.path__no { color: var(--blue); }
.path__name { font-family: var(--display); font-weight: 600; font-size: 1.05rem; letter-spacing: -0.01em; color: var(--ink); }
.path__dur { color: var(--graphite); }
.path__lane { position: relative; border-top: var(--rule) solid var(--hair); }
.path__lanename { position: absolute; left: 0; top: -0.6em; transform: translateY(-100%); color: var(--faint); }
.path__lane p { margin: 0; padding: 26px 16px 22px 0; font-size: .9rem; line-height: 1.45; color: var(--graphite); }
.path__lane p + p { padding-left: 16px; border-left: var(--rule) solid var(--hair); }
.path__lane--get p { color: var(--ink); }
.path__cell--gate { background: color-mix(in srgb, var(--blue) 6%, transparent); }
.path__lane { padding-top: 14px; }
.path__lanename { top: 10px; transform: none; }
@media (max-width: 980px) {
  .path__line { display: none; }
  .path__head { padding-top: 0; }
  .path__head, .path__lane { grid-template-columns: minmax(0, 1fr); }
  .path__band + .path__band, .path__lane p + p { padding-left: 0; border-left: 0; }
  .path__lane p { padding: 10px 0; }
}

/* the gate, opened: findings in, three tests, two outcomes */
.gate { display: grid; grid-template-columns: 1fr 3fr 1.2fr; gap: 0; margin-top: 40px; border: var(--rule) solid var(--hair); border-radius: 2px; }
.gate__in, .gate__out { padding: 22px; display: flex; flex-direction: column; gap: 10px; }
.gate__in { border-right: var(--rule) solid var(--hair); }
.gate__count { font-family: var(--display); font-size: 2.6rem; line-height: 1; font-weight: 500; color: var(--ink); }
.gate__tests { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.gate__test { padding: 22px; display: flex; flex-direction: column; gap: 6px; font-size: .88rem; color: var(--graphite); }
.gate__test + .gate__test { border-left: var(--rule) solid var(--hair); }
.gate__test b { color: var(--ink); font-weight: 600; }
.gate__no { color: var(--blue); }
.gate__out { border-left: var(--rule) solid var(--hair); justify-content: center; gap: 16px; }
.gate__result { display: flex; flex-direction: column; gap: 4px; padding-left: 12px; border-left: 2px solid var(--faint); font-size: .9rem; }
.gate__result b { color: var(--ink); font-weight: 600; }
.gate__result--in { border-left-color: var(--blue); }
@media (max-width: 980px) {
  .gate { grid-template-columns: minmax(0, 1fr); }
  .gate__in, .gate__out { border: 0; border-top: var(--rule) solid var(--hair); }
  .gate__tests { grid-template-columns: minmax(0, 1fr); }
  .gate__test + .gate__test { border-left: 0; border-top: var(--rule) solid var(--hair); }
}

/* --- 07 the team (parallel session) -------------------------------------- */
/* After 21st.dev's team-one: three equal columns, the portrait full-bleed on
   top of each card and the words underneath. The lead keeps the two-column
   opening the fellows strip already uses, so the sheet reads as three rows
   of the same rhythm rather than a sidebar with cards bolted to it. */
.team { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-areas: "lead" "cards" "fellows"; gap: clamp(28px, 4vw, 56px); align-items: start; }
.team__lead { grid-area: lead; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 18px clamp(28px, 4vw, 64px); align-items: start; }
.team__lead .body { max-width: none; }
.team__grid { grid-area: cards; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.team__fellows { grid-area: fellows; margin-top: 8px; padding-top: 22px; border-top: var(--rule) solid var(--hair); display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 18px 64px; align-items: start; }
.team__fellows .body { grid-column: 2; max-width: none; }
.team__fellows > .spec { grid-column: 1; }
.team__logos { grid-column: 2; display: flex; gap: 28px; font-family: var(--display); font-weight: 600; font-size: 1.1rem; letter-spacing: -0.01em; color: var(--ink); }

/* The card carries no padding of its own: the portrait bleeds to the hairline
   and the body pays it back. person__fig is the drawn stand-in that shows
   through until a real portrait file lands (the <img> removes itself on 404). */
.person { flex-direction: column; gap: 0; padding: 0; }
.person__tile { position: relative; width: auto; aspect-ratio: 4 / 5; max-height: 400px; border-bottom: var(--rule) solid var(--hair); background: color-mix(in srgb, var(--ink) 6%, var(--paper)); display: grid; place-items: center; overflow: hidden; }
.person__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.person__photo ~ .person__fig { display: none; } /* the drawn stand-in only shows when there is no portrait */
.person__fig { width: 30px; height: 30px; margin-top: 10px; border-radius: 50% 50% 4px 4px; background: var(--orange); position: relative; }
.person__fig::before { content: ""; position: absolute; left: 50%; bottom: 100%; width: 16px; height: 16px; margin-bottom: 4px; transform: translateX(-50%); border-radius: 50%; background: var(--orange); }
.person__body { display: flex; flex-direction: column; gap: 8px; padding: clamp(20px, 2.2vw, 28px); }
.person__name { font-family: var(--display); font-weight: 600; font-size: 1.15rem; letter-spacing: -0.015em; margin: 0; }
.person__role { color: var(--graphite); }
.person .body { max-width: none; font-size: .9rem; }
@media (max-width: 980px) {
  .team__lead, .team__fellows { grid-template-columns: minmax(0, 1fr); }
  .team__fellows .body, .team__logos, .team__fellows > .spec { grid-column: 1; }
  .person__body { padding: 18px; }
}
@media (max-width: 720px) {
  .team__grid { grid-template-columns: minmax(0, 1fr); }
  .person__tile { aspect-ratio: 16 / 9; max-height: 260px; }
}

/* --- the top bar (parallel session) -------------------------------------- */
/* One surface: the bar dissolves into the page instead of ruling it off.
   Links in the same sans as the buttons; the one action the site has sits
   on the right; on the homepage the bar says where you are, like the rail. */
.top { border-bottom: 0; background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; }
.top::before {
  content: ""; position: absolute; inset: 0 0 -28px 0; z-index: -1; pointer-events: none;
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  -webkit-backdrop-filter: saturate(1.3) blur(12px); backdrop-filter: saturate(1.3) blur(12px);
  -webkit-mask-image: linear-gradient(to bottom, #000 0, #000 62px, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0, #000 62px, transparent 100%);
}
.top { position: sticky; }
.top__in { gap: 28px; }
.nav { gap: 22px; margin-left: auto; }
.nav a {
  font-family: var(--display); font-size: 13.5px; font-weight: 500; letter-spacing: -0.005em;
  text-transform: none; color: var(--graphite); padding: 6px 0; border-radius: 0;
  position: relative; transition: color .16s ease;
}
.nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 1.5px;
  background: var(--blue); transform: scaleX(0); transform-origin: left; transition: transform .22s cubic-bezier(.22,.61,.36,1);
  display: block; margin: 0;
}
.nav a:hover { color: var(--ink); background: transparent; }
.nav a:hover::after, .nav a:focus-visible::after { transform: scaleX(1); }
.nav a[aria-current="page"] { color: var(--ink); }
.nav a[aria-current="page"]::after { transform: scaleX(1); background: var(--blue); height: 1.5px; }
.top__where { color: var(--graphite); letter-spacing: .1em; margin-left: 12px; opacity: 0; transition: opacity .3s ease; white-space: nowrap; }
.top__where:not([hidden]) { opacity: 1; }
.top__where b { color: var(--blue); font-weight: 500; margin-right: 8px; }
.btn--sm { --cap: 34px; font-size: 13.5px; padding-left: 16px; }
.btn--sm .btn__arrow { margin-left: 12px; font-size: 14px; box-shadow: 0 0 0 2px var(--paper); }
.top__cta { flex: none; }
@media (max-width: 860px) {
  .top__where { display: none; }
  .top__cta { display: none; }
  .nav a { padding: 8px 0; }
}

/* top bar: brand bigger, nav centred, action top right */
.top__in { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 24px; height: 68px; }
.top__left { display: flex; align-items: center; gap: 18px; min-width: 0; }
.nav { margin-left: 0; justify-self: center; }
.top__cta { justify-self: end; }
.brand { font-size: 18px; gap: 11px; letter-spacing: -0.015em; }
.brand__mark { width: 20px; height: 20px; border-width: 2px; }
.brand__mark::after { inset: 4px 4px auto auto; width: 7px; height: 7px; }
.top__where { margin-left: 0; }
@media (max-width: 860px) {
  .top__in { display: flex; height: auto; }
  .top__left { width: 100%; }
  .brand { width: auto; }
}
.brand__mark::after { inset: 2px 2px auto auto; } /* the orange sits up in the top-right of the box */

/* --- 01 to Will's image (hero session, 2026-08-25) ------------------------ */
/* No card: the number, bar and citation on the left; the sentence at full
   h2 size with the argument under it on the right. Overrides the card-era
   rules above. */
.stat--count .stat__cite { display: block; }
.stat--count .stat__lead { margin-top: 0; max-width: 20ch; font-size: clamp(1.9rem, 3.6vw, 3rem); }
.stat--count .stat__copy { padding-top: clamp(0px, 3vw, 28px); gap: 22px; }
.stat--count .stat__copy .body { max-width: 58ch; }
/* 01, again: the sentence beside the number, the copy level with it, a button under the copy */
.stat--row .stat__line { display: flex; align-items: center; gap: clamp(18px, 2.4vw, 34px); }
.stat--row .stat__lead { margin: 0; max-width: 15ch; font-size: clamp(1.35rem, 2.1vw, 1.85rem); line-height: 1.12; }
.stat--row .stat__copy { padding-top: 0; justify-content: center; gap: 20px; }
/* The button goes under the paragraph, with its foot on the same line as the
   foot of the bar opposite. Both columns are subgrids of the same three row
   tracks (figure / bar / key), so that alignment is structural: the button
   spans the first two tracks and sits at the end of them, which is exactly
   where the bar ends. No tuned margin to re-tune when the copy changes. */
/* row-gap 0: the grid's rows are now the left column's own three parts, and
   the 80px column gutter between figure, bar and key would tear them apart. */
.stat--row { align-items: start; grid-template-rows: auto auto auto; row-gap: 0; }
/* The left column is a subgrid, so the grid's three row tracks ARE figure,
   bar and key. That is what gives the button on the right something real to
   align to. */
.stat--row > .rise:first-child {
  display: grid; grid-template-rows: subgrid; grid-row: 1 / span 3;
}
.stat--row > .stat__copy { grid-column: 2; grid-row: 1; padding-top: 0; gap: 20px; }
/* Spanning tracks 1-2 (rather than sitting in track 2) means the button never
   stretches the bar's own row: its foot lands on the bar's foot. */
.stat--row > .stat__cta {
  grid-column: 2; grid-row: 1 / 3;
  align-self: end; justify-self: start; margin: 0;
}

/* One column below 900: nothing to align to, so the button just follows the
   paragraph. */
@media (max-width: 900px) {
  .stat--row { row-gap: 32px; }
  .stat--row > .rise:first-child { display: flex; flex-direction: column; }
  .stat--row > .stat__copy { grid-column: 1; }
  .stat--row > .stat__cta { grid-column: 1; grid-row: auto; align-self: start; }
}

/* --- 03 how we work: the graph (parallel session) ------------------------ */
/* The section pins for a few screens; scroll draws the curve. */
.sheet--track { padding-block: 0; }
.track { position: relative; height: 340vh; }
.track__pin {
  position: sticky; top: 68px; height: calc(100vh - 68px);
  display: flex; flex-direction: column; gap: clamp(12px, 2vh, 22px);
  padding-top: clamp(16px, 3vh, 32px); padding-bottom: clamp(16px, 3vh, 28px); box-sizing: border-box;
}
.sheet--track .titleblock { margin-bottom: 0; }
.graph { position: relative; flex: 1 1 auto; min-height: 220px; }
.graph__svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.graph__grid line { stroke: var(--hair); stroke-width: 1; }
.graph__area { fill: url(#graph-fill); }
.graph__curve { fill: none; stroke: var(--blue); stroke-width: 2.5; stroke-linecap: round; }
.graph__mark line { stroke: var(--blue); stroke-width: 1; stroke-dasharray: 3 4; opacity: 0; transition: opacity .3s ease; }
.graph__mark circle { fill: var(--paper); stroke: var(--blue); stroke-width: 2; opacity: 0; transition: opacity .3s ease, r .3s ease; }
.graph__mark.is-on line, .graph__mark.is-on circle { opacity: 1; }
.graph__mark.is-now circle { fill: var(--blue); }
.graph__labels { position: absolute; left: 0; right: 0; bottom: 0; display: grid; grid-template-columns: repeat(5, 1fr); transform: translateY(100%); padding-top: 8px; }
.graph__labels span { font-family: var(--spec); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint); text-align: center; }
.phases { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px; margin-top: 34px; flex: none; }
.phase {
  border: var(--rule) solid var(--hair); border-radius: 2px; padding: 16px 16px 18px;
  display: flex; flex-direction: column; gap: 6px; font-size: .84rem; line-height: 1.45; color: var(--graphite);
  opacity: 0; transform: translateY(12px); transition: opacity .45s ease, transform .45s cubic-bezier(.22,.61,.36,1), border-color .3s ease;
}
.phase.is-on { opacity: 1; transform: none; }
.phase.is-now { border-color: var(--blue); }
.phase__no { color: var(--faint); }
.phase__meta { display: flex; align-items: baseline; gap: 10px; margin: 0; }
.phase__name { font-family: var(--display); font-weight: 600; font-size: 1rem; letter-spacing: -0.01em; margin: 0; color: var(--ink); }
.phase__when { color: var(--blue); }
.phase__dur { color: var(--graphite); }
.phase p:last-child { margin: 0; }
@media (max-width: 980px) {
  .track { height: auto; }
  .track__pin { position: static; height: auto; }
  .graph { min-height: 200px; }
  .phases { grid-template-columns: minmax(0, 1fr); }
  .phase { opacity: 1; transform: none; }
  .graph__mark line, .graph__mark circle { opacity: 1; }
}

/* the rail, inside the pin: draws to the bottom first, then the curve takes the scroll */
.track__line { position: absolute; left: calc(var(--gutter) / 2 - 1px); top: 0; width: 2px; height: 100%; pointer-events: none; display: none; }
@media (min-width: 1100px) { .track__line { display: block; } }
.track__line::before { content: ""; position: absolute; left: 0; top: 0; width: 100%; height: var(--q, 0%); background: var(--blue); }
.track__line::after { content: ""; opacity: 0; position: absolute; left: 50%; top: var(--q, 0%); width: 9px; height: 9px; transform: translate(-50%, -50%); border-radius: 50%; background: var(--blue); box-shadow: 0 0 0 4px color-mix(in srgb, var(--blue) 22%, transparent); }
.track__line.is-live::after { opacity: 1; }
/* 02: the note runs the full width of the column, two lines. */
.layers__note { max-width: none; }

/* --- 03 the climb ---------------------------------------------------------- */
/* The graph is a window onto a line that keeps rising. The camera holds
   the drawing tip at one spot; gates arrive there with their card beside
   them and slide away behind as the climb goes on. */
.graph--climb { overflow: hidden; }
.graph--climb .graph__svg { overflow: hidden; }
.graph--climb .graph__grid line { stroke: var(--hair); stroke-width: 1; }
.graph--climb .graph__mark text { font-family: var(--spec); font-size: 10.5px; letter-spacing: .1em; fill: var(--faint); text-transform: uppercase; }
.graph--climb .graph__mark.is-now text { fill: var(--ink); }
.graph--climb .graph__mark line, .graph--climb .graph__mark circle, .graph--climb .graph__mark text { opacity: 0; transition: opacity .35s ease; }
.graph--climb .graph__mark.is-on line, .graph--climb .graph__mark.is-on circle, .graph--climb .graph__mark.is-on text { opacity: 1; }
.phases--climb { position: absolute; inset: 0; margin: 0; display: block; pointer-events: none; }
.phases--climb .phase {
  position: absolute; left: 0; top: 0; width: min(340px, 34%); margin: 0;
  background: color-mix(in srgb, var(--ink) 4%, var(--paper));
  opacity: 0; transform: translate(0, 0); transition: opacity .3s ease, border-color .3s ease;
  will-change: transform;
}
.phases--climb .phase.is-on { opacity: var(--fade, 1); }
@media (max-width: 980px) {
  .graph--climb { min-height: 260px; }
  .phases--climb { position: static; display: grid; gap: 14px; margin-top: 18px; }
  .phases--climb .phase { position: static; width: auto; opacity: 1 !important; transform: none !important; }
}

/* 03, smaller: the pin hugs its content and the graph is ~46vh, four or five grid lines tall */
.track__pin { height: auto; min-height: 0; }
.track { height: 300vh; }
.graph { flex: none; height: 46vh; min-height: 260px; max-height: 520px; }

/* the section's line starts at the corner the rail parks on, 26px above the section */
.track__line { top: -26px; height: calc(100% + 26px); }

/* 03: less scroll — the climb takes about one screen of travel */
.track { height: 190vh; }

/* 03 unpinned: a normal block; the graph is driven by the reader line passing through it */
.track { height: auto; }
.track__pin { position: static; }
.sheet--track { padding-bottom: clamp(48px, 6vw, 96px); }

/* 03: half a screen beneath the graph, so 04 stays below the fold until the journey is done */
.sheet--track { padding-bottom: 50vh; }

/* 03 held: the block sticks from the moment the crossing lands (reader line
   = 50vh, corner 26px above the section, 180px of sweep → top: 50vh − 154px)
   until the curve is done, and fills down to the viewport's bottom so 04
   cannot show beneath it. Travel = 60vh of scroll. */
.sheet--track { padding-bottom: 0; }
.track { height: calc(50vh + 154px + 60vh); }
.track__pin { position: sticky; top: calc(50vh - 154px); height: calc(50vh + 154px); box-sizing: border-box; }
@media (max-width: 980px) { .track { height: auto; } .track__pin { position: static; height: auto; } }

/* 03, locked: no sticky. The block sits in the page; when the crossing has
   landed on its corner the page stops and the wheel drives the line down
   the left and the curve across. The block reaches the viewport's bottom
   so 04 is not seen until it is done. */
.track { height: auto; }
.track__pin { position: relative; top: auto; height: calc(50vh + 154px); box-sizing: border-box; }
.track__line { display: block; }
@media (max-width: 980px) { .track__pin { height: auto; } .track__line { display: none; } }

/* while 03 has the wheel the document itself is frozen — cancelling wheel
   events is not enough on a trackpad, where a gesture that began before the
   corner landed cannot be cancelled mid-stream */
html { scrollbar-gutter: stable; }
html.is-locked { overflow: hidden; }

/* while the document is frozen the sticky bar loses its scrollport: pin it */
html.is-locked .top { position: fixed; top: 0; left: 0; right: 0; }
html.is-locked body { padding-top: 68px; }   /* keep the flow where it was */

.track__pin { height: calc(50vh + 194px); }   /* to the viewport's bottom and a little past, so 04 stays hidden */

/* 04 locked under the bar: the block starts 92px down (site.js NAVGAP) and
   runs to the viewport's bottom, so the heading is at the top of the screen
   before the curve starts and the whole graph is visible while it draws. */
.track__pin { height: calc(100vh - 108px); }

/* the graph's label row needs air beneath it before the crossing to 05, but
   the crossing keeps its standard 26px above the titleblock — so the room
   goes under the section, moving the whole boundary down and leaving every
   section's gap the same. */
.sheet--track { padding-bottom: 26px; }

/* the section's own line runs from the corner above it to the corner below,
   where the rail crosses to 04 (its titleblock sits 40px in, the crossing 26px above that) */
.track__line { height: calc(100% + 26px + 14px); }

/* 03 copy: a heading and one line above the graph; the current gate's label in ink */
.track__lead { display: flex; flex-direction: column; gap: 12px; max-width: 60ch; }
.track__h2 { font-size: clamp(1.6rem, 2.8vw, 2.4rem); max-width: 22ch; }
.track__lede { margin: 0; }
.graph { height: 42vh; }
.graph--climb .graph__mark text { fill: var(--faint); }
.graph--climb .graph__mark.is-now text { fill: var(--ink); }
.graph--climb .graph__mark.is-on:not(.is-now) text { fill: var(--graphite); }

/* 03: the box ends well above the section's bottom, clear of the crossing to 04 */
.graph { height: 34vh; min-height: 220px; }

/* 03: the graph fills the section down to its bottom edge, so the label row sits just above the crossing */
.graph { height: auto; flex: 1 1 auto; min-height: 220px; max-height: none; }

/* the bar's section label needs the room the centred nav wants: below 1080px
   it collided with the links, so it goes rather than crowds them */
@media (max-width: 1080px) { .top__where { display: none; } }

/* narrow: the six cards carry the phases on their own — the curve has no room
   to be read, and its absolute SVG was drawing across them */
@media (max-width: 980px) {
  .graph--climb .graph__svg { display: none; }
  .graph--climb { min-height: 0; }
  .phases--climb { margin-top: 0; }
}
@media (max-width: 980px) { .track__pin, .track { height: auto; } }   /* must follow the fixed-height rule above */

/* --- one spacing rule for the section openings ---------------------------
   Every sheet's first content block sits 113px below its crossing: the
   titleblock, then its standard margin. 02 and 03 were the two that broke
   it — 02 by centring the grid (which pushed the heading down inside an
   on-grid block), 03 by a vh-based pad above the titleblock and a flex gap
   in place of the margin. */
.sheet--flip .layers__grid { align-items: start; }
.sheet--flip .plate { align-self: center; }          /* the drawing stays centred, the heading does not */
.track__pin { padding-top: 0; gap: 0; }
.sheet--track .titleblock { margin-bottom: clamp(18px, 2.4vw, 30px); }
.track__lead { margin-bottom: clamp(12px, 2vh, 22px); }



/* --- sheet 02: the drawing set, one card at a time ------------------------- */
/* After 21st.dev's "images scrolling animation" (minhxthanh) — cards that
   travel with the scroll — but paired to the copy: the section holds still
   while one column of three sheets passes the frame, and the note on the
   right is whichever sheet is under the centre line. Sticky, not a wheel
   hijack: the reader keeps their scroll.

   Everything that only makes sense once the script is running is gated on
   .is-live, which the script adds. Without it — no JS, or narrow — this is a
   plain stack of three captioned cards with all three notes beside them, and
   that is a correct page. (Do NOT gate on .no-js: it is set on <body> but
   removed from <html>, so it never actually comes off.) */
.scroller { position: relative; height: 190vh; }   /* replaced by a measured px height once live */
.scroller__stage {
  position: sticky; top: 92px;               /* clear of the sticky header */
  height: min(74vh, 660px);
  display: flex; align-items: center;
}
.scroller__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 4.5vw, 80px); align-items: center; width: 100%;
}

/* the window: one card fills it, its neighbours show at the fold */
.scroller__frame { height: min(74vh, 660px); padding: 0; }
/* The fold at top and bottom is painted OVER the column, not masked out of
   it. A mask forces the whole frame — three large drawings — through a
   compositing pass on every frame it moves; a static overlay in the plate's
   own colour costs nothing once painted. */
.scroller__frame::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(to bottom, var(--sheet) 0, transparent 6%),
    linear-gradient(to top,    var(--sheet) 0, transparent 6%);
}
.scroller__col {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: 26px;
  padding: 26px;
  transform: translate3d(0, 0, 0);   /* its own layer, so scrolling it is a composite */
  backface-visibility: hidden;
}
.scroller.is-live .scroller__col { will-change: transform; }
.scroller__card {
  margin: 0; position: relative; flex: 0 0 auto;
  border: var(--rule) solid var(--hair); border-radius: 2px;
  background: var(--paper); overflow: hidden;
  transition: opacity .35s ease, border-color .35s ease;
}
/* off-centre sheets sit back; the one being described comes forward. Opacity
   only — a filter here re-rasterises a 400px drawing on every frame. */
.scroller.is-live .scroller__card { opacity: .26; }
.scroller.is-live .scroller__card.is-on { opacity: 1; border-color: var(--blue); }
/* Every card image is cut to this ratio at export, and the box is given the
   same ratio — so cover has nothing to crop and the whole drawing is visible.
   A fixed pixel height here was silently trimming a quarter off the sides. */
.scroller__card img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 1600 / 1104;
  object-fit: cover; object-position: center;
}
.scroller__card figcaption {
  display: flex; align-items: baseline; gap: 14px;
  padding: 12px 14px; border-top: var(--rule) solid var(--hair);
}
.scroller__card .spec { color: var(--faint); }
.scroller__card.is-on .spec { color: var(--blue); }
.scroller__card-name { font-size: 0.9rem; color: var(--graphite); }
.scroller__card.is-on .scroller__card-name { color: var(--ink); }

.scroller__copy { display: flex; flex-direction: column; gap: 22px; align-items: flex-start; }
/* The per-card beats are a phone thing: site.js clones them into the cards, and
   the notes block beside the column is what says this everywhere else.
   Scoped through .scroller__card because there is a vestigial
   `.scroller__card figcaption` rule above for a caption this markup never had,
   and (0,1,1) beats a bare .scroller__cap. */
.scroller__card .scroller__cap { display: none; }
.scroller__copy .t-h2 { max-width: 17ch; margin: 0; }
.scroller__steps { display: grid; gap: 24px; width: 100%; }
.scroller__step { display: flex; flex-direction: column; gap: 10px; }
.scroller__step-no { color: var(--blue); }
.scroller__step .body { margin: 0; max-width: 46ch; }

/* live: the three notes share one cell, so the block never changes height */
.scroller.is-live .scroller__steps { gap: 0; }
.scroller.is-live .scroller__step {
  grid-area: 1 / 1; opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .4s ease, transform .4s cubic-bezier(.22,.61,.36,1);
}
.scroller.is-live .scroller__step.is-on { opacity: 1; transform: none; pointer-events: auto; }

.scroller__marks { display: none; gap: 8px; }
.scroller.is-live .scroller__marks { display: flex; }
.scroller__mark { width: 26px; height: 2px; background: var(--hair); transition: background .3s ease; }
.scroller__mark.is-on { background: var(--blue); }

@media (prefers-reduced-motion: reduce) {
  .scroller__card, .scroller.is-live .scroller__step { transition-duration: .01ms; }
}

/* Narrow: no room for a two-column pairing, and 280vh of pin is a long way to
   hold someone on a phone. Plain stack — same content, read normally.
   Bounded at 620 because below that the section becomes a horizontal sweep
   instead (see the phone block at the end of this file); the `transform: none
   !important` here would otherwise beat the sweep's own transform. */
@media (min-width: 620px) and (max-width: 980px) {
  .scroller { height: auto; }
  .scroller__stage { position: static; height: auto; display: block; }
  .scroller__grid { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .scroller__frame { height: auto; -webkit-mask-image: none; mask-image: none; }
  .scroller__col { transform: none !important; padding: 18px; gap: 18px; }
}

/* --- 05 the audit deck ------------------------------------------------------ */
/* Replaces the ladder (31 Aug). Five sheets of the deliverable in a horizontal
   frame; the note opposite is whichever sheet is in it.

   The movement is native scroll-snap on .audit__track, not a transform: the
   trackpad, a touch drag and the scrollbar all work before site.js has done
   anything, and the JS only has to read which page landed. Everything the JS
   adds is a class — with it switched off the reader still gets all five
   sheets by dragging, with all five notes stacked under the heading. */
.audit__grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(28px, 4.5vw, 80px); align-items: center;
}
.audit__copy { display: flex; flex-direction: column; gap: 22px; align-items: flex-start; }
.audit__copy .t-h2 { max-width: 15ch; margin: 0; }

/* the five notes share one cell, so the drawing opposite never shifts */
.audit__steps { display: grid; width: 100%; }
.audit__step { display: flex; flex-direction: column; gap: 10px; }
.audit__step-no { color: var(--blue); }
.audit__step .body { margin: 0; max-width: 46ch; }
.audit.is-live .audit__step {
  grid-area: 1 / 1; opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .4s ease, transform .4s cubic-bezier(.22,.61,.36,1);
}
.audit.is-live .audit__step.is-on { opacity: 1; transform: none; pointer-events: auto; }
/* no JS: the notes read as a list rather than a stack of one */
.audit:not(.is-live) .audit__steps { gap: 24px; }

/* --- the controls ----------------------------------------------------------- */
.audit__nav { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.audit__arrow {
  width: 38px; height: 38px; flex: none; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--ink);
  border: var(--rule) solid var(--hair); border-radius: 2px;
  font-size: 1rem; line-height: 1; cursor: pointer;
  transition: border-color .2s ease, color .2s ease, background .2s ease;
}
.audit__arrow:hover:not(:disabled) { border-color: var(--blue); color: var(--blue); }
.audit__arrow:disabled { opacity: .34; cursor: default; }
.audit__arrow:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; }
.audit__count { color: var(--faint); font-variant-numeric: tabular-nums; }
.audit__count [data-count] { color: var(--blue); }

.audit__marks { display: flex; gap: 8px; margin-left: auto; }
.audit__mark {
  width: 26px; height: 2px; padding: 0; border: 0; cursor: pointer;
  background: var(--hair); transition: background .3s ease;
  /* a 2px hit target is not a hit target: the box is padded out invisibly */
  box-sizing: content-box; border-block: 12px solid transparent;
  background-clip: content-box;
}
.audit__mark:hover { background: var(--faint); }
.audit__mark.is-on { background: var(--blue); }
.audit__mark:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* --- the frame -------------------------------------------------------------- */
.audit__frame { padding: 0; }
.audit__track {
  position: relative; z-index: 1;
  display: flex; gap: 0;
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;                 /* the ticks and arrows are the affordance */
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;        /* flicking the deck never navigates back */
}
.audit__track::-webkit-scrollbar { display: none; }
.audit__track:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }

.audit__page {
  margin: 0; flex: 0 0 100%; max-width: 100%;
  scroll-snap-align: center; scroll-snap-stop: always;
  padding: clamp(20px, 2.6vw, 34px);
  display: flex; flex-direction: column; gap: 14px;
  transition: opacity .35s ease;
}
.audit.is-live .audit__page { opacity: .3; }
.audit.is-live .audit__page.is-on { opacity: 1; }
.audit__page .pg {
  display: block; width: 100%; height: min(60vh, 470px);
  border: var(--rule) solid var(--hair); border-radius: 2px;
  background: var(--paper);
}
.audit__page.is-on .pg { border-color: var(--blue); }
.audit__page figcaption { display: flex; align-items: baseline; gap: 14px; }
.audit__page .spec { color: var(--faint); }
.audit__page.is-on .spec { color: var(--blue); }
.audit__page-name { font-size: .9rem; color: var(--graphite); }
.audit__page.is-on .audit__page-name { color: var(--ink); }

/* --- the drawn sheet -------------------------------------------------------- */
/* Line mock-ups of the deliverable, in the site's drawing set: grey is what
   exists, blue is what we are pointing at. Everything is on tokens, so the
   sheets follow the theme rather than staying stubbornly white. */
.pg__paper { fill: var(--paper); }
.pg__rule  { stroke: var(--hair); stroke-width: 1; }
.pg__rule--soft { stroke: var(--hair-soft); }
.pg__box   { fill: none; stroke: var(--hair); stroke-width: 1.5; }
.pg__box--hot { stroke: var(--blue); fill: color-mix(in srgb, var(--blue) 8%, transparent); }
.pg__arrow { fill: none; stroke: var(--faint); stroke-width: 1.5; }
.pg__arrow--back { stroke-dasharray: 4 4; }
.pg__bar   { fill: color-mix(in srgb, var(--graphite) 26%, transparent); }
.pg__bar--hot { fill: var(--blue); }
.pg__bar--out { fill: color-mix(in srgb, var(--graphite) 12%, transparent); }
.pg__tick  { fill: none; stroke: var(--blue); stroke-width: 2.5; stroke-linecap: square; }
.pg__mile  { stroke: var(--blue); stroke-width: 1; stroke-dasharray: 3 4; }
.pg__diamond { fill: var(--blue); }

.pg__h { font-family: var(--display); font-weight: 500; font-size: 19px; letter-spacing: -.02em; fill: var(--ink); }
.pg__t { font-family: var(--spec); font-size: 12.5px; fill: var(--graphite); }
.pg__t--hot { fill: var(--blue); }
.pg__t--dim { fill: var(--faint); }
.pg__spec { font-family: var(--spec); font-size: 10.5px; letter-spacing: .1em; fill: var(--faint); }
.pg__spec--hot { fill: var(--blue); }
.pg__spec--dim { fill: var(--faint); }
.pg__spec--r, .pg__t--r { text-anchor: end; }

@media (prefers-reduced-motion: reduce) {
  .audit__page, .audit.is-live .audit__step { transition-duration: .01ms; }
  .audit__track { scroll-behavior: auto; }
}

/* Narrow: the pairing stacks — note above, deck below — and the deck keeps
   working exactly as it does wide. The sheet is portrait, so it is the height
   that has to give first. */
@media (max-width: 980px) {
  .audit__grid { grid-template-columns: minmax(0, 1fr); gap: 30px; }
  .audit__copy { order: 1; }
  .audit__frame { order: 2; }
  .audit__page { padding-top: 34px; }   /* clear of the plate tag */
  .audit__page .pg { height: clamp(300px, 74vw, 460px); }
  .audit__marks { margin-left: 0; }
}

/* --- manifesto theses ---------------------------------------------------- */
/* A thesis is not a station: no spec table, no status chip. Number in the
   left rail, claim wide, argument in one measured column, and one concrete
   property anchor so the page never floats free of the job. */
/* Closed, the nine are a list; the old manifesto gap left them stranded. Tight
   rows, and an open one buys its own breathing room. */
.theses { display: grid; gap: clamp(26px, 2.6vw, 40px); }
.thesis:has(.disc[open]) { padding-bottom: clamp(12px, 1.6vw, 26px); }
.thesis {
  display: grid;
  grid-template-columns: 92px minmax(0, 1fr);
  gap: clamp(16px, 3vw, 40px);
  border-top: var(--rule) solid var(--hair);
  padding-top: 22px;
  scroll-margin-top: 84px;
}
@media (max-width: 820px) {
  .thesis { grid-template-columns: minmax(0, 1fr); gap: 12px; }
}
.thesis__no { padding-top: 6px; }
/* Claim spans the full measure; argument and anchor sit side by side so the
   right half of the sheet carries marginalia instead of nothing. */
/* One column now: the claim is a <summary> and the argument/anchor pair sits
   in .disc__panel, which carries the two-column measure this used to. */
.thesis__body { min-width: 0; }
.thesis__claim { max-width: 24ch; margin: 0; }
.thesis__arg {
  max-width: 56ch; color: var(--graphite); margin: 0; text-wrap: pretty;
  font-size: clamp(1rem, 1.15vw, 1.08rem); line-height: 1.55;
}
.thesis__arg strong { color: var(--ink); font-weight: 600; }
/* Mono, but not shouting — the anchor is read, not scanned. */
.thesis__anchor {
  max-width: 56ch; margin: 2px 0 0;
  border-left: 2px solid var(--blue); padding: 3px 0 3px 14px;
  font-family: var(--spec); font-size: 12px; line-height: 1.5;
  letter-spacing: .01em; text-transform: none; color: var(--graphite);
}
.thesis__anchor b {
  display: block; margin-bottom: 5px;
  font-size: 10px; font-weight: 400; letter-spacing: .12em;
  text-transform: uppercase; color: var(--blue);
}

/* --- the scrubber (approach) --------------------------------------------- */
/* A minimap of the nine, parked in the left margin. After Ruixen's Chapter
   Scrubber on 21st.dev — the ticks and the preview card, without the
   spring-magnify dock wave, which reads as a flourish next to the plates.
   Built from the DOM so it can never drift from the theses; the blue index
   panel is the no-JS contents. */
.scrub {
  position: fixed; left: 18px; top: 50%; transform: translateY(-50%);
  z-index: 30; display: none; gap: 0;
}
@media (min-width: 1180px) { .scrub { display: grid; } }
.scrub__tick { display: block; position: relative; padding: 5px 0; text-decoration: none; }
.scrub__tick::before {
  content: ""; display: block; width: 16px; height: 2px;
  background: var(--faint);
  transition: width .18s ease, background-color .18s ease;
}
.scrub__tick.is-past::before { background: color-mix(in srgb, var(--blue) 45%, transparent); }
.scrub__tick.is-on::before { width: 30px; background: var(--blue); }
.scrub__tick:hover::before,
.scrub__tick:focus-visible::before { width: 30px; background: var(--ink); }
.scrub__tick:focus-visible { outline: none; }
.scrub__label {
  position: absolute; left: 42px; top: 50%;
  transform: translateY(-50%) translateX(-5px);
  width: max-content; max-width: 30ch;
  opacity: 0; pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: var(--rule) solid var(--hair); border-radius: 2px;
  padding: 8px 11px; font-size: .82rem; line-height: 1.3;
  color: var(--ink); text-wrap: pretty;
}
.scrub__label b {
  display: block; margin-bottom: 4px;
  font-family: var(--spec); font-size: 10px; font-weight: 400;
  letter-spacing: .12em; color: var(--blue);
}
.scrub__tick:hover .scrub__label,
.scrub__tick:focus-visible .scrub__label { opacity: 1; transform: translateY(-50%); }
@media (prefers-reduced-motion: reduce) {
  .scrub__tick::before, .scrub__label { transition-duration: .01ms; }
}

/* --- 04: the climb axis in orange ---------------------------------------- */
/* Two colours doing two jobs: blue is the line being drawn, orange is the
   people. Every stop on this axis — one scope, upskill, ownership — is a
   promise about them, so the droplines and their labels take the orange and
   the curve keeps the blue. */
.graph--climb .graph__mark line { stroke: var(--orange); }
.graph--climb .graph__mark text { fill: color-mix(in srgb, var(--orange) 80%, transparent); }
.graph--climb .graph__mark.is-now text { fill: var(--orange); }

/* --- disclosures (approach) ---------------------------------------------- */
/* The nine read as a list of claims first; the argument is one click away.
   Native <details>, so the page still works with JS off and Ctrl+F still
   finds text inside a closed panel in Chrome. The affordance is a hairline
   plus, not a chevron — the drawing set has no chevrons in it. */
.disc { border: 0; }
.disc__sum {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  gap: clamp(16px, 3vw, 40px);
  cursor: pointer;
  list-style: none;
  padding: 2px 0 6px;
  transition: opacity .2s ease;
}
.disc__sum::-webkit-details-marker { display: none; }
.disc__sum:hover { opacity: .74; }
.disc__sum:focus-visible {
  outline: 2px solid var(--blue); outline-offset: 6px; border-radius: 2px;
}

/* The cue sits on the baseline of the claim, mono and quiet. */
.disc__cue {
  display: inline-flex; align-items: center; gap: 10px;
  padding-top: 8px; white-space: nowrap;
  font-family: var(--spec); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--blue);
}
@media (max-width: 640px) { .disc__cue-txt { display: none; } }
.disc__cue-txt::after { content: attr(data-shut); }
.disc[open] .disc__cue-txt::after { content: attr(data-open); }

/* A plus that loses its upright when the panel opens. */
.disc__mark {
  position: relative; display: block; width: 15px; height: 15px;
  border: var(--rule) solid currentColor; border-radius: 50%;
}
.disc__mark::before,
.disc__mark::after {
  content: ""; position: absolute; inset: 50% 3px auto 3px;
  height: 1px; background: currentColor; transform: translateY(-50%);
  transition: transform .22s cubic-bezier(.22,.61,.36,1);
}
.disc__mark::after { transform: translateY(-50%) rotate(90deg); }
.disc[open] .disc__mark::after { transform: translateY(-50%) rotate(0deg); }

/* Argument left, anchor right — the two-column measure the closed body used
   to carry, moved inside the panel so .thesis__body can stay one column. */
.disc__panel {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  column-gap: clamp(24px, 4vw, 64px);
  row-gap: 16px;
  align-items: start;
  padding-top: 10px;
}
@media (max-width: 1100px) { .disc__panel { grid-template-columns: minmax(0, 1fr); } }
/* The columns are narrow: a cue beside a two-line heading fights it, so it
   drops under the rule instead. */
.disc--col .disc__panel { grid-template-columns: minmax(0, 1fr); padding-top: 4px; }
.disc--col .disc__sum { grid-template-columns: minmax(0, 1fr); gap: 10px; padding-bottom: 0; }
.disc--col .disc__cue { padding-top: 0; }
.disc--col .disc__panel p { margin: 0; }

/* Height animation where the browser supports it; a hard open elsewhere. */
@supports (interpolate-size: allow-keywords) {
  :root { interpolate-size: allow-keywords; }
  .disc::details-content {
    block-size: 0; overflow: hidden;
    transition: block-size .32s cubic-bezier(.22,.61,.36,1), content-visibility .32s allow-discrete;
  }
  .disc[open]::details-content { block-size: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .disc::details-content, .disc__mark::before, .disc__mark::after { transition: none; }
}

/* --- phone: the structure worked out in Framer ---------------------------- */
/* Ported from the Innocent Spinach build (1 Sep). Everything here is scoped to
   620px and below, which is the breakpoint that build used; it changes nothing
   about the desktop or tablet page. See
   website/website-v1/handoff/FRAMER-REFRESH-PLAN.md for why each one is here. */
@media (max-width: 619px) {

  /* 01 — the paragraph earns the click, so it comes before the button.
     The CTA is already a direct child of the grid (it is placed against the
     grid's own row tracks on desktop), so order alone does it. */
  #stat-95 { display: flex; flex-direction: column; }
  #stat-95 > .rise:first-child { order: 0; }
  #stat-95 .stat__copy { order: 1; }
  #stat-95 .stat__cta { order: 2; margin-top: 6px; }

  /* 05 — the drawn sheets come off. They are line mock-ups at roughly A4 with
     10–12px labels; at this width the type inside them cannot be read, so the
     frame is a large outlined box carrying nothing legible. The notes still
     change: site.js drives them from the controls directly when the track is
     not on the page. */
  .audit__frame { display: none; }
  .audit__nav { margin-bottom: 4px; }

  /* 07 — the founder cards lose the box: portrait, name, role, bio. */
  .person { border: 0; background: transparent; }
  .card--grid::before { display: none; }

  /* 08 — three claims meant to be read against each other become one swipe
     track rather than three screens of scrolling. Native scroll-snap, so it
     works with a finger before site.js has done anything. */
  /* :has, so this only touches the three-column shape. /contact reuses .three
     as a one-column wrapper around its spec list, and a carousel rule that
     matched it would turn that list into a sideways scroller. */
  .three:has(> .three__col) {
    display: flex; flex-direction: row;
    justify-content: flex-start; align-items: stretch;
    overflow-x: auto; scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
    scrollbar-width: none; gap: 18px;
  }
  .three:has(> .three__col)::-webkit-scrollbar { display: none; }
  .three__col {
    flex: 0 0 84%; scroll-snap-align: start; scroll-snap-stop: always;
  }

  /* 02 — the pinned vertical column becomes a horizontal sweep, each drawing
     carrying its own beat underneath it, so the note and the picture it belongs
     to are read together rather than in two separate blocks. After 21st.dev's
     horizontal-scroll-carousel. site.js clones each step into its card. */
  .scroller__grid { display: flex; flex-direction: column; gap: 0; }
  /* the copy is dissolved for this breakpoint so the heading, the frame, the
     ticks and the button can be ordered against each other — they are in two
     different columns on desktop and cannot be reordered as siblings */
  .scroller__copy { display: contents; }
  .scroller__copy > .t-h2 { order: 0; margin-bottom: 22px; }
  .scroller__frame { order: 1; height: auto; padding: 0; overflow: hidden; }
  .scroller__frame::after { display: none; }   /* the fold is for the vertical column */
  .scroller__marks { order: 2; display: flex; margin-top: 18px; }
  .scroller__copy > .btn { order: 3; margin-top: 22px; }
  .scroller__steps { display: none; }
  .scroller.is-live .scroller__stage { position: sticky; }
  .scroller__col {
    flex-direction: row; gap: 14px; padding: 0;
    transform: translate3d(0, 0, 0);
  }
  .scroller__card {
    flex: 0 0 78%; display: flex; flex-direction: column; gap: 12px;
    border: 0; background: none; overflow: visible;
  }
  .scroller.is-live .scroller__card { opacity: .34; }
  .scroller.is-live .scroller__card.is-on { opacity: 1; }
  .scroller__card img {
    border: var(--rule) solid var(--hair); border-radius: 2px;
    transition: border-color .35s ease;
  }
  .scroller.is-live .scroller__card.is-on img { border-color: var(--blue); }
  /* same specificity fight as above, plus the vestigial figcaption rule's
     padding and rule-line, which this caption does not want */
  .scroller__card .scroller__cap {
    display: flex; flex-direction: column; gap: 8px;
    padding: 0; border-top: 0;
  }
  .scroller__card .scroller__cap .body { margin: 0; font-size: .95rem; }

  /* 09 — white fields on the dark page. */
  .field input, .field textarea, .field select {
    background-color: #ffffff; color: #0d1117; border-color: #d6dae1;
  }
  .field input::placeholder, .field textarea::placeholder { color: #8a94a3; }

  /* /contact leads with the spec rows, not a second lede. */
  .contact__lede { display: none; }
}

/* --- the phone drawer ---------------------------------------------------- */
/* The 860 block above turned the nav into a second row rather than hiding it,
   because an earlier attempt hid it with no replacement and left phones unable
   to reach four of five pages. This is that replacement: the links go into a
   drawer instead of a second row, which also brings back the "Free discovery"
   action that has been hidden below 860 ever since. */
.top__menu { display: contents; }
.top__toggle { display: none; }

@media (max-width: 860px) {
  .top__in { flex-wrap: nowrap; height: 62px; padding-block: 0; gap: 16px; }
  .top__left { width: auto; }

  .top__toggle {
    display: inline-flex; align-items: center; justify-content: center;
    margin-left: auto; flex: none;
    width: 40px; height: 40px; padding: 0;
    background: transparent; border: 0; cursor: pointer; color: var(--ink);
  }
  .top__toggle:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
  .top__bars { display: grid; gap: 4px; width: 20px; }
  .top__bars span {
    display: block; height: 1.5px; background: currentColor; border-radius: 1px;
    transition: transform .22s cubic-bezier(.22,.61,.36,1), opacity .16s ease;
  }
  .top.is-open .top__bars span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
  .top.is-open .top__bars span:nth-child(2) { opacity: 0; }
  .top.is-open .top__bars span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

  /* the drawer. visibility rather than display, so it can transition and so a
     closed drawer is out of the tab order */
  .top__menu {
    display: flex; flex-direction: column; align-items: stretch; gap: 4px;
    position: absolute; top: 100%; left: 0; right: 0;
    padding: 8px var(--gutter) 20px;
    background: var(--paper);
    border-bottom: var(--rule) solid var(--hair);
    visibility: hidden; opacity: 0; transform: translateY(-6px);
    transition: opacity .2s ease, transform .22s cubic-bezier(.22,.61,.36,1),
                visibility 0s linear .22s;
  }
  .top.is-open .top__menu {
    visibility: visible; opacity: 1; transform: none;
    transition-delay: 0s, 0s, 0s;
  }

  /* the row rules from the 860 block above do not apply inside a drawer */
  .nav {
    width: 100%; margin-left: 0; gap: 0;
    flex-direction: column; overflow: visible; padding-bottom: 0;
  }
  .nav a { padding: 12px 0; font-size: 12.5px; border-bottom: var(--rule) solid var(--hair-soft); }
  .nav a:last-child { border-bottom: 0; }
  .nav a:hover { background: transparent; color: var(--blue); }

  /* the action comes back, at the foot of the drawer */
  .top__cta { display: inline-flex; align-self: flex-start; margin-top: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .top__menu, .top__bars span { transition-duration: .01ms; }
}
