/* ============================================================
   TAD & NO SONS — the table
   One concept: a deck that deals itself. One verb: the turn.
   Fields invert; cards never do (physical-object law).
   ============================================================ */

/* ---------- type — the authored pairing ----------
   display: Instrument Serif (the card-index didone, self-hosted 15KB)
   utility: the grotesk chrome. Two faces, one accent. */
@font-face {
  font-family: 'Instrument Serif';
  src: url('../assets/fonts/instrument-serif-latin.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Instrument Serif';
  src: url('../assets/fonts/instrument-serif-latin-italic.woff2') format('woff2');
  font-weight: 400; font-style: italic; font-display: swap;
}

/* ---------- ground ---------- */
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: auto; overflow-x: clip; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

/* ---------- chrome voice — the printer's imprint (Fix 0) ----------
   Two cuts of the same face; body.chrome-a / body.chrome-b picks.
   A · THE IMPRINT: cartouche lettering — small, spaced, engraved-plate.
   B · THE INDEX: corner-index letterforms — larger, tight, present. */
body.chrome-a { --chrome-font: 'Instrument Serif', Georgia, serif; --chrome-track: 0.18em; --chrome-size: clamp(11px, 0.76vw, 13px); }
body.chrome-b { --chrome-font: 'Instrument Serif', Georgia, serif; --chrome-track: 0; --chrome-size: clamp(16px, 1.25vw, 20px); }
body.chrome-b .chassis__corner, body.chrome-b .colophon { font-variant-numeric: normal; }
.chassis__corner, .colophon, .chapter__index, .cut__line, .cut__skip,
.scene__hint, .court__meta, .pocket-ace__hint, .tap-hint {
  font-family: var(--chrome-font);
  letter-spacing: var(--chrome-track);
  font-size: var(--chrome-size);
  font-weight: 400;
}

body {
  background: var(--field, var(--stock));
  color: var(--onfield, var(--ink));
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
  transition: background-color 700ms var(--ease-deal), color 700ms var(--ease-deal);
  cursor: none;
}
@media (pointer: coarse) { body { cursor: auto; } }

/* the four worlds — chapter changes the table, never the card */
body[data-chapter="0"]  { --field: var(--stock); --onfield: var(--ink); }
body[data-chapter="i"]  { --field: var(--field-i);   --onfield: var(--on-field-i); }
body[data-chapter="ii"] { --field: var(--field-ii);  --onfield: var(--on-field-ii); }
body[data-chapter="iii"]{ --field: var(--field-iii); --onfield: var(--on-field-iii); }
body[data-chapter="iv"] { --field: var(--field-iv);  --onfield: var(--on-field-iv); }
/* the imprint world — ABOUT's fourth room (R2) */
body[data-chapter="brick"] { --field: var(--field-brick); --onfield: var(--on-field-brick); }

/* stock tooth — one runtime overlay, whole site, blended per ground */
body::after {
  content: ""; position: fixed; inset: 0; z-index: 2000; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: 0.05; mix-blend-mode: multiply;
}
body[data-chapter="iii"]::after,
body[data-chapter="brick"]::after { mix-blend-mode: screen; opacity: 0.045; }

::selection { background: var(--red); color: var(--stock); }
body[data-chapter="ii"] ::selection,
body[data-chapter="iv"] ::selection { background: var(--ink); color: var(--stock); }

:focus { outline: none; }
:focus-visible {
  outline: 2px solid currentColor; outline-offset: 6px; border-radius: 2px;
}
[data-turnable]:focus-visible { outline-style: dashed; }

/* ---------- THE CUT (loader) ---------- */
.cut {
  position: fixed; inset: 0; z-index: 3000;
  background: var(--ink); color: var(--stock);
  display: grid; place-items: center; overflow: hidden;
}
.cut__riffle { position: absolute; inset: 0; display: grid; place-items: center; }
.cut__riffle .card__back-copy {
  position: absolute; width: min(38vw, 240px); aspect-ratio: var(--card-ratio);
  border-radius: 10px; background: var(--red) center / cover no-repeat;
  background-image: url('../assets/final/specials-web/back_850.webp');
  box-shadow: 0 0 0 1px var(--ink), 8px 10px 0 rgba(0,0,0,.35);
  will-change: transform;
}
/* R2 2026-08-11: moved out of the centre. The line is --stock and the riffle
   fan is white card stock sitting dead centre — the line painted ON TOP of it
   and was white-on-white, invisible for ~1s of a ~1.8s loader. It now sits in
   the lower third, clear of the fan, which is also where CUT THE DECK ↓ lives
   on the hero: the grammar already existed. */
.cut__line {
  position: absolute; z-index: 2; bottom: 20vh; left: 50%; translate: -50% 0;
  letter-spacing: .42em; font-size: var(--type-chrome); font-weight: 500;
  white-space: nowrap; text-align: center;
}
.cut__skip {
  position: absolute; right: 24px; bottom: 20px; z-index: 3;
  background: none; border: 1px solid currentColor; color: inherit;
  font: inherit; font-size: var(--type-chrome); letter-spacing: .3em;
  padding: 8px 14px; border-radius: 999px; cursor: none;
}
.cut__skip:hover { background: var(--stock); color: var(--ink); }
.cut--done { pointer-events: none; }

/* ---------- CHASSIS ---------- */
.chassis__corner {
  position: fixed; z-index: 900; font-size: var(--type-chrome);
  letter-spacing: .3em; font-weight: 500; color: var(--onfield, var(--ink));
  transition: color 700ms var(--ease-deal);
  font-variant-numeric: tabular-nums;
}
.chassis__corner--tl { top: 18px; left: 22px; }
/* the works clock rewrites its text every frame while RHYTHM fast-forwards it:
   contained, so each write lays out and paints this corner and nothing else */
.chassis__corner--tr { top: 18px; right: 22px; contain: layout paint; }
.chassis__corner--bl { bottom: 18px; left: 22px; }
.chassis__corner--br { bottom: 18px; right: 22px; }

/* the imprint doubles as the way home on every page but the front door */
a.chassis__mark { color: inherit; text-decoration: none; }
a.chassis__mark:hover, a.chassis__mark:focus-visible { color: var(--red); }
body[data-chapter="iii"] a.chassis__mark:hover,
body[data-chapter="brick"] a.chassis__mark:hover { opacity: .62; color: inherit; }

/* pinned face-down card — R2: this is now THE DEAL's trigger (the menu) */
.pocket-ace {
  position: fixed; z-index: 970; right: 22px; top: 50%;
  translate: 0 -50%; rotate: 6deg;
  width: 44px; aspect-ratio: var(--card-ratio);
  text-decoration: none; display: block;
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  cursor: none;
}
@media (pointer: coarse) { .pocket-ace { cursor: pointer; } }
.pocket-ace[aria-expanded="true"] .pocket-ace__back { rotate: -14deg; translate: -6px 0; }
.pocket-ace__back {
  position: absolute; inset: 0; border-radius: 5px;
  background: var(--red) center / cover no-repeat;
  background-image: url('../assets/final/specials-web/back_850.webp');
  box-shadow: 0 0 0 1px var(--ink), 4px 5px 0 rgba(26,21,18,.28);
  transition: rotate 300ms var(--ease-snap), translate 300ms var(--ease-snap);
}
.pocket-ace:hover .pocket-ace__back,
.pocket-ace:focus-visible .pocket-ace__back { rotate: -8deg; translate: -4px 0; }
.pocket-ace__hint {
  position: absolute; top: 50%; right: calc(100% + 10px); translate: 0 -50%;
  font-size: 10px; letter-spacing: .3em; color: var(--onfield, var(--ink));
  opacity: 0; transition: opacity 200ms var(--ease-deal);
  white-space: nowrap;
}
.pocket-ace:hover .pocket-ace__hint, .pocket-ace:focus-visible .pocket-ace__hint { opacity: 1; }

/* ---------- SCENES ---------- */
.scene { position: relative; min-height: 100vh; display: grid; place-items: center; padding: 12vh 6vw; }
.scene__hint {
  position: absolute; bottom: 26px; left: 50%; translate: -50% 0;
  font-size: var(--type-chrome); letter-spacing: .4em;
}

/* D2 — the clearance law: header in flow, card starts var(--clear) below the word.
   One grid for every chapter; the word may bleed the right EDGE, never a card. */
.chapter__head { position: static; padding: 0 0 var(--clear); z-index: 5; }
.chapter__index { display: block; margin-bottom: 1.2em; }
.chapter__name {
  font-family: 'Instrument Serif', Georgia, serif; font-weight: 400;
  font-size: clamp(72px, 14.5vw, 220px); line-height: .86; letter-spacing: 0;
  white-space: nowrap;
}

/* ---------- THE CARD ---------- */
/* shadow = printed offset, not framework soft-glow: one crisp ink ring + one hard drop */
.card {
  position: relative; width: min(66vw, 380px); aspect-ratio: var(--card-ratio);
  border-radius: 12px; background: var(--stock);
  box-shadow: 0 0 0 1px rgba(26,21,18,.35), 10px 14px 0 rgba(26,21,18,.14);
  will-change: transform;
  transform: rotate(0.001deg); /* pre-promote */
}
body[data-chapter="iii"] .card, body[data-chapter="iv"] .card {
  box-shadow: 0 0 0 1px rgba(26,21,18,.6), 12px 16px 0 rgba(0,0,0,.35);
}
.card__plate { position: absolute; inset: 0; width: 100%; height: 100%; border-radius: inherit; display: block; }
.card--hero { width: min(74vw, 460px); }

.card__back-face {
  position: absolute; inset: 0; border-radius: inherit;
  background: var(--red) center / cover no-repeat;
  background-image: url('../assets/final/specials-web/back_850.webp');
}

/* the copy pair — caps upright, lowercase reversed; the mirror as type */
.turncopy { display: grid; gap: .5em; max-width: 18ch; position: relative; z-index: 4; }
.turncopy__up {
  font-family: 'Instrument Serif', Georgia, serif; font-weight: 400;
  font-size: var(--type-deal); line-height: .92; letter-spacing: 0.005em;
}
.turncopy__down {
  font-family: 'Instrument Serif', Georgia, serif; font-style: italic; font-weight: 400;
  font-size: calc(var(--type-deal) * .5); line-height: 1.12;
  opacity: 0; translate: 0 0.6em; rotate: 0.001deg;
}
.is-turned .turncopy__down, .turncopy.is-turned .turncopy__down { opacity: 1; translate: 0 0; }
.is-turned .turncopy__up, .turncopy.is-turned .turncopy__up { opacity: .25; }
.turncopy__up, .turncopy__down {
  transition: opacity 450ms var(--ease-snap), translate 450ms var(--ease-snap);
}

/* ---------- HERO ---------- */
/* D1: a long runway — the turn completes, then the "master of none" beat
   HOLDS before release.
   R2-F1 (2026-08-09): the hold ran long. Trimmed 15% — 108vh of hold to
   92vh — while leaving the TURN's runway untouched at 162vh, so the beat
   itself is unchanged and only the dwell after it is shorter.
   162 + 92 = 254vh, and the turn now completes at 162/254 = 0.638
   (TURN_END in js/main.js — the two numbers must move together). */
.scene--hero { min-height: 254vh; align-items: start; }
.hero__stage {
  position: sticky; top: 0; min-height: 100vh; width: 100%;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; justify-items: center;
  gap: 4vw;
}
.hero__copy { grid-column: 3; justify-self: start; }
.card--hero { grid-column: 2; }

/* (R2-CUT 2026-08-08: THE RIDE and every .ride__* rule deleted; the horizontal
   axis change is gone with it.
   2026-10-20: the four chapters cut too — .scene--chapter, .chapter__stage,
   every .stage-props rule and GROWTH's deal. The hero deals straight into the
   court. Verbatim copy in backup/chapters/chapters.css.) */

/* ---------- THE COURT ---------- */
.scene--court { overflow: clip; align-items: start; display: block; padding: 10vh 6vw; }
.court {
  display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 6vw; width: min(1240px, 100%); align-items: center;
  position: relative; z-index: 2;
  margin-top: var(--clear);
}
.court__index { list-style: none; }
.court__row {
  position: relative; padding: 1.1em 0; border-bottom: 1px solid currentColor;
  overflow: hidden;
}
.court__row:first-child { border-top: 1px solid currentColor; }
.court__row-face, .court__row-truth {
  display: flex; align-items: baseline; justify-content: space-between; gap: 2ch;
  transition: translate 450ms var(--ease-snap), opacity 450ms var(--ease-snap);
}
.court__title {
  font-family: 'Instrument Serif', Georgia, serif; font-weight: 400;
  font-size: calc(var(--type-deal) * .72); letter-spacing: 0;
}
.court__meta  { font-size: var(--type-chrome); letter-spacing: .3em; }
.court__row-truth {
  position: absolute; inset: 0; padding: 1.1em 0; align-items: center;
  font-family: 'Instrument Serif', Georgia, serif; font-style: italic;
  font-size: calc(var(--type-deal) * .42);
  translate: 0 100%; opacity: 0; rotate: 0.001deg;
}
.court__row.is-turned .court__row-face { translate: 0 -100%; opacity: 0; }
.court__row.is-turned .court__row-truth { translate: 0 0; opacity: 1; }
.court__stage { display: grid; place-items: center; }
.card--stage { width: min(60vw, 330px); }

/* ---------- THE MAKER'S ACE ----------
   R2-F3 REBUILD (2026-08-09). The old staging failed three ways at once:
   (a) the stage was a TWO-ROW grid — card 600px over caption 388px plus a
       6vw gap = 1074px of content inside a 100vh box, and `place-content:
       center` centred the overflow, so the card presented ~145px BELOW the
       viewport centre and then clipped off the top as the pin engaged;
   (b) the section inherited `padding: 12vh` from .scene, so the sticky box
       began 12vh below where ScrollTrigger's `top top` fires — the pin and
       the scrub were measuring from different origins;
   (c) the card had a fixed pixel height, so at 1280x720 it was already
       clipped before the turn began.
   Now: ONE row, three columns — the card alone owns the centre line, and it
   is sized off the viewport so it can never outgrow the frame it is centred
   in. Pin travel is 200vh - 100vh = 100vh, and the turn completes at 85% of
   it (ACE_TURN_END in js/main.js), so the card finishes turning, HOLDS whole
   and centred for 15vh — the hero's grammar — and only then does the stage
   release and the footer arrive. Change one, change the other. */
.scene--ace { min-height: 200vh; display: block; padding: 0; }
.ace__stage {
  position: sticky; top: 0; height: 100svh;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  grid-template-rows: 1fr;
  align-items: center; justify-items: center;
  column-gap: clamp(20px, 3.4vw, 64px);
  width: min(1280px, 92vw); margin-inline: auto;
}
.scene--ace .chapter__head {
  grid-column: 1; justify-self: end; text-align: right; padding: 0;
}
.card--ace {
  grid-column: 2;
  /* height-led: the card is clamped by the viewport it is centred in */
  width: min(60vw, 372px, calc(70svh * 2 / 3));
  container-type: inline-size;
}
.ace__caption { grid-column: 3; justify-self: start; max-width: 14ch; }
/* the mailto lives in the plate's own empty band — DOM styled to the plate,
   printed upside-down at rest; the turn makes it read. Money path, dead straight. */
.ace__band {
  position: absolute; left: 17.9%; top: 75.4%; width: 64.5%; height: 6%;
  display: grid; place-items: center;
  transform: rotate(180deg);
  font-family: 'Instrument Serif', Georgia, serif;
  font-size: 4.7cqw; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink); text-decoration: none;
  font-variant-numeric: oldstyle-nums;
}
.ace__band:hover, .ace__band:focus-visible { color: var(--red); }
/* the caption must stay shorter than the card, or it steals the centre line back */
.ace__up { font-size: calc(var(--type-deal) * .5); }

/* ---------- COLOPHON ---------- */
.colophon {
  display: flex; justify-content: space-between; padding: 24px 22px 72px;
  font-size: var(--type-chrome); letter-spacing: .3em;
  font-variant-numeric: tabular-nums;
}

/* ---------- PIP CURSOR ---------- */
.pip-cursor {
  position: fixed; z-index: 4000; top: 0; left: 0; pointer-events: none;
  translate: -50% -50%; will-change: transform;
  mix-blend-mode: difference; color: #fff;
  visibility: hidden; /* revealed on first pointermove — no 0,0 ghost */
}
.pip-cursor.is-live { visibility: visible; }
.pip-cursor svg { display: block; fill: currentColor; transition: transform 300ms var(--ease-snap); }
.pip-cursor.is-over-turnable svg { transform: rotate(180deg) scale(1.4); }
@media (pointer: coarse) { .pip-cursor { display: none; } }

/* ---------- REDUCED MOTION — the diptych path ---------- */
@media (prefers-reduced-motion: reduce) {
  body { transition: none; }
  .cut { display: none; }
  .card { will-change: auto; }
  .turncopy__down { opacity: 1 !important; translate: 0 0 !important; }
  .turncopy__up { opacity: 1 !important; }
  .scene--hero, .scene--ace { min-height: 100vh; }
  [data-turnable] .card__plate { /* both readings shown side by side via the diptych util */ }
}

/* ---------- MOBILE — designed, not degraded ----------
   M-OVERFLOW root fix: no fixed-px display floors, every display word
   on-canvas, wrap allowed, zero horizontal pan. */
@media (max-width: 760px) {
  :root {
    --type-deal: clamp(28px, 8.6vw, 40px);
    --clear: 3.5vh;
  }
  .chapter__name { font-size: 17.5vw; white-space: normal; max-width: 100%; }
  .turncopy { max-width: 100%; }
  .turncopy__down { font-size: max(16px, calc(var(--type-deal) * .5)); margin-top: .35em; }

  .hero__stage {
    grid-template-columns: 1fr; grid-template-rows: 1fr auto; gap: 4vh;
    padding: 9vh 0 14vh; align-items: center;
  }
  .hero__copy { grid-column: 1; grid-row: 2; justify-self: center; text-align: center; padding: 0 6vw; }
  .card--hero { grid-column: 1; grid-row: 1; width: min(58vw, 300px); align-self: center; }
  .scene__hint { bottom: 58px; }

  .court { grid-template-columns: 1fr; }
  .court__stage { order: -1; }
  .pocket-ace { right: 18px; width: 36px; rotate: 4deg; }

  /* M2 — the cut fits the small table */
  .cut__riffle .card__back-copy { width: min(30vw, 150px); box-shadow: 0 0 0 1px var(--ink), 5px 6px 0 rgba(0,0,0,.35); }

  /* R2 — no ghost runways: tap replaces the scrub, so the scenes collapse to flow */
  .scene--hero { min-height: 100svh; }
  .scene--ace { min-height: auto; }

  /* R1 — the MAKER'S ACE rebuilt mobile-first: card whole and centered,
     copy stacked, mailto tappable. The contact card never breaks. */
  .scene--ace { display: block; padding: 8vh 6vw 12vh; }
  .ace__stage {
    position: static; height: auto; min-height: 0;
    display: grid; grid-template-columns: 1fr; gap: 3.5vh;
    place-items: center; place-content: start center; width: 100%; margin: 0;
  }
  .scene--ace .chapter__head { padding: 0; text-align: center; }
  .card--ace { width: min(74vw, 330px); }
  .ace__caption { text-align: center; max-width: 100%; justify-self: center; }
  .ace__band { font-size: 5cqw; }

  /* M5 — mobile chrome: two corners + a designed bottom bar, never squeezed desktop corners */
  .chassis__corner--tr, .chassis__corner--bl { display: none; }
  .chassis__corner--tl { top: 14px; left: 16px; }
  .chassis__corner--br { bottom: 14px; right: 16px; }
  .colophon {
    flex-direction: column; align-items: flex-start; gap: 12px;
    padding: 32px 16px 88px;
  }
}

/* M1 — the tap affordance, chrome register, self-kills after the first turn */
.tap-hint {
  display: none; margin-top: 14px; opacity: .75;
}
@media (pointer: coarse) {
  .tap-hint { display: block; }
  .tap-hint.is-done { display: none; }
}

/* ============================================================
   R2 · THE MENU (rebuilt 2026-08-15 — the card deal is deleted)
   The pinned card opens a full screen: the deck's own dark ink over a
   blurred page, two display words, each captioned in the chrome voice.
   HOVER = THE INK SWEEP. At rest the word is an unprinted outline; on
   hover ink floods it left to right. Chosen over turning the word 180°
   because a nav label that becomes unreadable while you point at it is a
   usability cost the site does not need — and the press laying ink is the
   same world as the turn without paying it.
   The sweep is TWO counter-running translates (window ← , text →) so the
   glyphs never move: transform-only, no clip-path, no layout, 60fps.
   ============================================================ */
.u-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* THE BLUR IS CUT, ON MEASUREMENT — see docs/ship-notes.md.
   backdrop-filter was briefed and built, then failed the 60fps law at 4x CPU
   throttle in every configuration tested (animated 0→16px: median 50ms/frame;
   static 16px: 34% of frames over 20ms; static 8px: 24%; none: 3%). A fifth
   variant — blur applied only once the overlay had settled — was abandoned
   without a number: it had not completed two open/close cycles after four
   minutes (the other four each did three cycles in ~30s) and was terminated.
   What replaces it: the ground stays PARTLY TRANSPARENT so the table is still
   present underneath — but at 95%, not 88%. Proofed at 88% the sharp card art
   read straight through and fought the outlined word, because quieting the
   backdrop was exactly the blur's job. 95% keeps the table as a ghost and
   gives the hairline outline a ground to sit on. The site grain (body::after,
   z-2000) still falls over the overlay, so it is not a flat panel.
   Nothing here animates but opacity. */
.menu {
  position: fixed; inset: 0; z-index: 960;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--ink) 95%, transparent);
  color: var(--stock);
  visibility: hidden; opacity: 0;
  transition: opacity 340ms var(--ease-exit), visibility 0s linear 340ms;
}
.menu.is-open {
  visibility: visible; opacity: 1;
  transition: opacity 300ms var(--ease-deal), visibility 0s;
}

.menu__hand {
  display: grid; gap: clamp(20px, 5vh, 58px);
  justify-items: center; text-align: center;
}
.menu__item {
  display: grid; justify-items: center; gap: .5em;
  text-decoration: none; color: inherit;
}
.menu__word {
  position: relative; display: block;
  font-family: var(--display-face); font-weight: var(--display-weight);
  font-size: clamp(54px, 11.5vw, 184px); line-height: .92;
  letter-spacing: var(--display-track); white-space: nowrap;
}
/* rest: the unprinted plate — outline only */
.menu__word-out {
  display: block; color: transparent;
  -webkit-text-stroke: 1.4px var(--stock);
}
/* the ink: a window that sweeps across while the glyphs stay put */
.menu__word-ink {
  position: absolute; inset: 0; overflow: hidden;
  transform: translateX(-100.5%);
  transition: transform 620ms var(--ease-snap);
  will-change: transform;
}
.menu__word-ink > span {
  display: block; color: var(--stock);
  transform: translateX(100.5%);
  transition: transform 620ms var(--ease-snap);
}
.menu__item:hover .menu__word-ink,
.menu__item:focus-visible .menu__word-ink,
.menu__item:hover .menu__word-ink > span,
.menu__item:focus-visible .menu__word-ink > span { transform: translateX(0); }

/* Touch has no hover, so on a phone the sweep would never fire and both
   words would sit permanently in the thin unprinted outline — degraded, not
   designed (§6.1). On coarse pointers the words ship INKED, and the press
   moment moves to :active: the word un-inks under the finger and floods
   back as you lift. Same mechanic, different input. */
@media (pointer: coarse) {
  .menu__word-ink, .menu__word-ink > span { transform: translateX(0); }
  .menu__item:active .menu__word-ink { transform: translateX(-100.5%); }
  .menu__item:active .menu__word-ink > span { transform: translateX(100.5%); }
  .menu__cap { opacity: .8; }
}

.menu__cap {
  font-family: var(--chrome-font); letter-spacing: var(--chrome-track);
  font-size: var(--chrome-size); color: var(--stock);
  opacity: .5; transition: opacity 300ms var(--ease-deal);
}
.menu__item:hover .menu__cap,
.menu__item:focus-visible .menu__cap { opacity: 1; }
.menu__item[aria-current="page"] .menu__word-out { -webkit-text-stroke-width: .8px; }

.menu__close {
  position: absolute; top: 3.2vh; right: 3vw;
  background: none; border: 1px solid currentColor; color: var(--stock);
  width: 44px; height: 44px; border-radius: 999px; cursor: none;
  font-family: var(--chrome-font); font-size: 15px; line-height: 1;
  display: grid; place-items: center;
  transition: background 220ms var(--ease-deal), color 220ms var(--ease-deal);
}
.menu__close:hover, .menu__close:focus-visible { background: var(--stock); color: var(--ink); }
@media (pointer: coarse) { .menu__close { cursor: pointer; } }

/* No ring inside the menu. The ink sweep IS a word's focus state and the fill
   is the close button's; the global outline only added a white box round the
   focused word (ABOUT, on every open). The overlay itself takes focus on a
   pointer opening and has nothing to show. */
.menu:focus-visible, .menu__item:focus-visible, .menu__close:focus-visible { outline: none; }

/* reduced motion — instant overlay, no blur animation, no sweep.
   Hover still marks the word, by ink rather than by movement. */
@media (prefers-reduced-motion: reduce) {
  .menu, .menu.is-open { transition: none; }
  .menu__word-ink, .menu__word-ink > span, .menu__cap, .menu__close { transition: none; }
  .menu__word-ink { transform: none; overflow: visible; opacity: 0; }
  .menu__word-ink > span { transform: none; }
  .menu__item:hover .menu__word-ink,
  .menu__item:focus-visible .menu__word-ink { opacity: 1; }
}

/* ============================================================
   R2 · THE SWALLOW — the cut between pages.
   Never a hard jump: the field closes over the table, the next page
   opens out of it. Transform-only, one paint layer.
   ============================================================ */
.swallow {
  position: fixed; inset: 0; z-index: 2500; pointer-events: none;
  background: var(--ink);
  transform: scaleY(0); transform-origin: 50% 100%;
  will-change: transform;
}
/* leaving: the field closes up over the table */
body.is-leaving .swallow {
  transform: scaleY(1);
  transition: transform 420ms var(--ease-exit);
}
/* arriving: set by the inline head script BEFORE first paint, so the
   destination never shows a bare frame; then it opens downward. */
html.pre-entering .swallow { transform: scaleY(1); transform-origin: 50% 0%; }
html.pre-entering.is-opened .swallow {
  transform: scaleY(0);
  transition: transform 560ms var(--ease-snap);
}
@media (prefers-reduced-motion: reduce) {
  .swallow { display: none; }
}

/* ============================================================
   R2 · ABOUT — the imprint world
   ============================================================ */
.scene--about-hero {
  min-height: 100vh; display: grid; place-items: center;
  padding: 12vh 6vw; position: relative; overflow: clip;
  isolation: isolate;
}
/* NOTE: no colour override lives here. When the plate lands, the hero's ground
   goes dark, and the honest fix is for the hero to OWN a dark world rather than
   to repaint its own type — otherwise the chassis chrome, ::selection and card
   shadows all stay on the light world and go illegible over the plate. The
   world swap is in js/main.js (worldOf), conditional on the plate being there. */
.about__stage {
  position: relative; width: min(1240px, 100%);
  display: grid; place-items: center; isolation: isolate;
}

/* the reserved visual slot: full-bleed BEHIND the display word.
   The composition is authored to stand with the plate and without it —
   nothing below moves when the asset lands. */
/* scene-level, so it is a true bleed rather than a band letterboxed into the
   stage's content height */
.about__plate {
  position: absolute; inset: 0; z-index: 0;
  display: grid; place-items: center; pointer-events: none;
}
.about__plate-img {
  width: 100%; height: 100%; object-fit: cover; display: block; opacity: .9;
}
/* The designed empty state is NOTHING. A hairline frame here read as a
   placeholder box on the proof — and round 1 already killed crop marks as
   noise. The composition is authored to carry the screen on type alone, so
   the honest empty state is an empty stage. The slot is still reserved and
   still visible in dev, so the reservation can be checked without ever
   showing a juror a box waiting to be filled. */
.about__plate-mark { display: none; }
body.devcopy .about__plate-mark {
  display: block; position: absolute; inset: 0;
  outline: 1px dashed var(--red); outline-offset: -8px;
}
body.devcopy .about__plate-mark::after {
  content: "RESERVED — about plate (TODO-ASSET)";
  position: absolute; left: 10px; top: 10px;
  font: 400 10px/1.4 var(--chrome-font); letter-spacing: .16em;
  background: var(--red); color: var(--stock); padding: 2px 6px; border-radius: 2px;
}

/* deliberately NOT a centred display + subline stack (CLAUDE.md §7): the word
   is set hard left and allowed to bleed the EDGE, the sub-line sits low-right
   in its counter-space. The composition is asymmetric on purpose. */
/* The word carries the screen alone — it has to, the plate may never come.
   Kicker top-left, ABOUT at plate scale bleeding the left margin, sub-line
   dropped low-right in the word's own counter-space. Never a centred stack. */
.about__head {
  position: relative; z-index: 2; width: 100%;
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  row-gap: var(--clear); align-items: end;
}
.about__head .chapter__index { grid-area: 1 / 1; }
.about__display {
  grid-area: 2 / 1 / 3 / 3;
  font-size: clamp(56px, 25vw, 400px); line-height: .8;
  margin-inline-start: -.05em; /* optical: the A's diagonal sits on the margin */
}
.about__sub {
  grid-area: 3 / 2; justify-self: end; text-align: right;
  font-family: 'Instrument Serif', Georgia, serif; font-style: italic;
  font-size: calc(var(--type-deal) * .42); max-width: 28ch; line-height: 1.24;
  text-wrap: balance;
}

/* the chapter frames: head pinned top, body set low-right. The scene is
   composed to the full frame — content is never left floating in a 100vh box. */
.scene--about {
  display: grid;
  /* .scene sets place-items:center — left unchecked it centres the display word
     off the margin. The chapter grid owns its own alignment. */
  justify-items: start; align-items: start; align-content: center;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  padding: 12vh 6vw; overflow: clip;
}
.scene--about .chapter__head { grid-area: 1 / 1 / 2 / 3; }
.about__frame { grid-area: 2 / 2 / 3 / 3; align-self: end; margin-top: var(--clear); }
.about__body {
  max-width: 52ch;
  font-family: 'Instrument Serif', Georgia, serif;
  font-size: clamp(19px, 1.5vw, 26px); line-height: 1.42;
  /* the signed copy is never cut to fit: the rag is fixed in layout instead.
     IDENTITY at 390 left "me." alone on its last line without this. */
  text-wrap: pretty;
}
.scene--about .chapter__name { font-size: clamp(64px, 13vw, 210px); }
/* RHYTHM is set in authored lines — one written line, one span, one beat */
.rhythm__line { display: block; }

/* ============================================================
   R3 · RHYTHM — /about chapter III, the climax (2026-10-21)
   Scroll is the drum. js/rhythm.js owns every number that moves; this
   block only lays the stage out. Who writes what (one system per property
   per element, always):
     .rhythm__shake          transform  ← the loop (the beat shake)
     .rhythm__col / ghosts   transform, opacity, visibility ← the loop
     .rhythm__quote (figure) transform  ← the loop (Act III kicker)
     .rhythm__w, .rhythm__cite  opacity, transform ← the drop timeline
     .rhythm__line           opacity, transform ← CSS transition on .is-in
     .rhythm__punch          transform  ← the land hit (GSAP)
   Colour is --rh-ink, which the flash swaps for two frames. Tokens only.
   ============================================================ */
.scene--rhythm {
  --rh-pin: 250;           /* mirrors CONFIG.pinVh — rhythm.js sets it before measuring */
  /* place-items: .scene centres its children, and engines now honour that in
     block layout too — the stage (all absolute children) shrank to 0px wide */
  display: block; padding: 0; place-items: normal;
  min-height: calc(100svh + var(--rh-pin) * 1vh);
}
.rhythm__stage {
  --rh-ink: var(--onfield, var(--stock));
  position: sticky; top: 0; width: 100%; height: 100svh; overflow: hidden;
  color: var(--rh-ink);
}
/* THE DROP's single flash: the section inverts for two frames, once per visit */
.rhythm__stage.is-flash { --rh-ink: var(--brick); background: var(--stock); }
.rhythm__shake {
  position: absolute; inset: 0;
  /* permanently its own layer: the beat writes translate3d and then clears it,
     and without this the layer was created and destroyed on every hit — each
     time re-rastering the whole stage (295ms in one trace, mid-drop, mobile) */
  will-change: transform;
}

.rhythm__columns { position: absolute; inset: 0; z-index: 0; pointer-events: none; user-select: none; }
/* the falling word — the menu's ABOUT / CONTACT face, verbatim (tokens.css) */
.rhythm__col {
  position: absolute; top: 0;
  writing-mode: vertical-rl;           /* reads top to bottom, a book's spine */
  white-space: nowrap; inline-size: max-content;
  font-family: var(--display-face); font-weight: var(--display-weight);
  letter-spacing: var(--display-track); line-height: .92;
  color: var(--rh-ink);
  transform-origin: 0 0; will-change: transform, opacity;
  opacity: 0; visibility: hidden;      /* the loop reveals each one once fonts are in */
}
/* back columns: the unprinted plate — outline only, like the menu word at rest */
.rhythm__col--back { color: transparent; -webkit-text-stroke: 1px var(--rh-ink); }

.rhythm__head { position: absolute; top: 12vh; left: 6vw; z-index: 2; }

/* THE DROP — the quote. Positioned by a wrapper that never moves, so the
   loop is the only thing that ever writes the figure's transform. */
.rhythm__quote-pos {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  display: grid; align-content: center; justify-items: start; padding: 0 6vw;
}
.rhythm__quote { transform-origin: 0 0; will-change: transform; pointer-events: auto; }
.rhythm__words {
  font-family: var(--display-face); font-weight: var(--display-weight);
  letter-spacing: var(--display-track);
  font-size: clamp(44px, 7.4vw, 132px); line-height: .9;
  max-width: 9.2em; text-wrap: balance;
}
.rhythm__w { display: inline-block; transform-origin: 50% 70%; }
.rhythm__cite {
  margin-top: .5em; text-align: right;
  font-family: var(--display-face); font-size: clamp(22px, 2.3vw, 36px);
}
.rhythm__cite cite { font-style: italic; }

/* ACT III–IV — the release column. The slot is the kicker's landing place;
   rhythm.js sizes it to the quote at kicker scale. */
.rhythm__release {
  position: absolute; top: 0; bottom: 0; left: 52vw; right: 7vw; z-index: 3;
  display: grid; align-content: center;
}
.rhythm__slot { height: 0; }
.rhythm__body {
  font-family: 'Instrument Serif', Georgia, serif;
  font-size: clamp(20px, 1.85vw, 30px); line-height: 1.36;
}
.rhythm__line { text-wrap: balance; }
.rhythm__line--land {
  margin-top: .4em; font-style: italic; font-size: 1.55em; line-height: 1.1;
}
.rhythm__punch { display: inline-block; transform-origin: 0 70%; }

/* motion path only (rhythm.js adds .is-live): everything waits for its beat */
.scene--rhythm.is-live .rhythm__w,
.scene--rhythm.is-live .rhythm__cite { opacity: 0; }
.scene--rhythm.is-live .rhythm__line {
  opacity: 0; transform: translate3d(0, .5em, 0);
  transition: opacity 360ms var(--ease-snap), transform 520ms var(--ease-snap);
}
.scene--rhythm.is-live .rhythm__line.is-in { opacity: 1; transform: none; }

/* ?debug=rhythm */
.rhythm-debug {
  position: fixed; right: 76px; top: 52px; z-index: 4100;
  background: var(--ink); color: var(--stock);
  font: 400 11px/1.6 ui-monospace, Menlo, monospace; white-space: pre;
  padding: 10px 12px; border-radius: 4px; pointer-events: none;
  box-shadow: 0 0 0 1px rgba(246,243,234,.25);
}

@media (max-width: 760px) {
  .rhythm__head { top: 9vh; }
  /* the pinned menu card sits on the right edge: type clears it (36px + 18px inset) */
  .rhythm__quote-pos { padding: 0 62px 0 6vw; }
  .rhythm__words { font-size: 12.5vw; max-width: none; }
  .rhythm__cite { font-size: 21px; }
  .rhythm__release { left: 6vw; right: 62px; }
  .rhythm__body { font-size: 19px; line-height: 1.38; }
  /* the works clock is hidden on phones — while RHYTHM is on screen it takes the
     empty bottom-right corner, on its own ground so a column can't swallow it */
  body.is-rhythm-clock .chassis__corner--tr {
    display: block; top: auto; bottom: 10px; right: 12px;
    padding: 4px 4px 4px 6px; background: var(--field);
  }
}

/* REDUCED MOTION — the designed path: no fall, no shake, no flash, no clock.
   Static MOVING columns behind, the quote and the body, each fading in once. */
@media (prefers-reduced-motion: reduce) {
  .scene--rhythm { min-height: 0; }
  .rhythm__stage { position: relative; height: auto; }
  .rhythm__shake {
    position: relative; inset: auto; min-height: 100vh;
    display: grid; align-content: center; gap: calc(var(--clear) * 2);
    padding: 12vh 6vw;
  }
  .rhythm__head, .rhythm__quote-pos, .rhythm__release { position: relative; inset: auto; padding: 0; z-index: 2; }
  .rhythm__release { justify-self: end; width: min(44vw, 640px); }
  .rhythm__col { transform: none; }
  .scene--rhythm.is-rm .rhythm__quote,
  .scene--rhythm.is-rm .rhythm__release { opacity: 0; transition: opacity 800ms var(--ease-deal); }
  .scene--rhythm.is-rm .is-in { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) and (max-width: 760px) {
  .rhythm__shake { padding: 9vh 62px 9vh 6vw; }
  .rhythm__release { justify-self: stretch; width: auto; }
}

/* ============================================================
   R2 · DEV — the placeholder overlay (never ships visible).
   Alt+P, or ?dev=1. Marks every copy zone with its status so Tad can
   see exactly what is frozen v1, what is an empty slot, and what the
   ego audit has flagged. Zero cost when off: one body class.
   ============================================================ */
body.devcopy [data-copy] {
  outline: 1px dashed var(--red); outline-offset: 4px; position: relative;
}
body.devcopy [data-copy="frozen"] { outline-color: #2A54A6; }
body.devcopy [data-copy="ego"]    { outline-style: dotted; outline-width: 2px; }
body.devcopy [data-copy]::after {
  content: attr(data-copy);
  position: absolute; top: -6px; left: 0; translate: 0 -100%;
  font: 400 10px/1.4 var(--chrome-font); letter-spacing: .16em;
  text-transform: uppercase; white-space: nowrap;
  background: var(--red); color: var(--stock);
  padding: 2px 6px; border-radius: 2px; pointer-events: none; z-index: 3000;
}
body.devcopy [data-copy="frozen"]::after { background: #2A54A6; }
body.devcopy [data-copy="ego"]::after    { background: var(--ink); }
.devpanel {
  position: fixed; left: 16px; bottom: 16px; z-index: 4100;
  background: var(--ink); color: var(--stock);
  font: 400 11px/1.7 var(--chrome-font); letter-spacing: .1em;
  padding: 12px 14px; border-radius: 4px; max-width: 300px;
  box-shadow: 0 0 0 1px rgba(246,243,234,.25);
}
.devpanel b { letter-spacing: .2em; display: block; margin-bottom: 6px; }
.devpanel span { display: inline-block; width: 9px; height: 9px; margin-right: 7px; border-radius: 2px; }

/* ---------- ABOUT + DEAL — mobile designed, not squeezed ---------- */
@media (max-width: 760px) {
  .menu__word { font-size: 15vw; }
  .menu__hand { gap: 5vh; }
  .menu__close { top: 2.4vh; right: 5vw; }

  /* the pinned trigger is fixed at the right edge — running text must clear it
     or the menu card sits in the middle of a sentence. Card 36px + 18px inset. */
  .about__body { padding-right: 62px; }

  .scene--about-hero { padding: 10vh 6vw; }
  .about__plate { inset: -10vh -6vw; }
  /* mobile: the counter-space column can't exist at 390px — the sub-line drops
     under the word and keeps the left edge. Designed, not squeezed. */
  .about__head { grid-template-columns: 1fr; }
  .about__display { grid-area: 2 / 1; font-size: 27vw; margin-inline-start: -.03em; }
  .about__sub {
    grid-area: 3 / 1; justify-self: start; text-align: left;
    font-size: max(16px, calc(var(--type-deal) * .5)); max-width: 30ch;
  }
  .scene--about {
    grid-template-columns: 1fr; padding: 11vh 6vw 10vh;
  }
  .scene--about .chapter__head { grid-area: 1 / 1 / 2 / 2; }
  .about__frame { grid-area: 2 / 1 / 3 / 2; }
  .scene--about { padding: 9vh 6vw; }
  .about__frame { grid-template-columns: 1fr; }
  .about__body { grid-column: 1; max-width: 100%; font-size: 18px; }
  .scene--about .chapter__name { font-size: 17.5vw; }
  .devpanel { left: 10px; bottom: 10px; max-width: 62vw; }
}
