/* ═══════════════════════════════════════════════════════════════════════════
   CALLOUTS — DESIGN TOKENS
   The argument for every value in this file is docs/DESIGN-LANGUAGE.md.
   This is the ONLY file allowed to contain a literal colour, size or duration.
   Three layers, per ds-design-tokens-structure:
       1 PRIMITIVE   raw values, context-free, never referenced by a component
       2 SEMANTIC    purpose-named, references primitives only
       3 COMPONENT   surface-named, references semantics only
   ═══════════════════════════════════════════════════════════════════════════ */

:root {

/* ─── 1 · PRIMITIVE ─────────────────────────────────────────────────────────

   COLOUR IS AUTHORED IN OKLCH. Every grey carries a few thousandths of chroma,
   because a grey with zero chroma reads as a default and a grey with chroma
   reads as a material. There are two materials and they lean opposite ways:

     GRAPHITE  hue 262, the cool blue-grey of a pencil mark in bulk. This is
               the chrome — the bench the work sits on.
     PAPER     hue  85, the warm grey of printed stock. This is the ink on the
               chrome, and the sheet a note is written on over the frame.

   The ramp is deliberately not a 50→950 gradient. It is two short ladders,
   one per material, each rung with a job.

   🔴 EVERY HEX IN A COMMENT BELOW IS DERIVED FROM THE VALUE BESIDE IT. It is not a
   second declaration and it is never the thing to edit: the oklch is what renders.
   `board.load_tokens` recomputes all 27 on every run and
   `test_the_documented_hex_is_derived_not_authored` blocks on a disagreement.

   ⚠️ FIVE OF THEM WERE STALE FOR FOUR DAYS while board.py named them every run and
   nothing stopped. --graphite-400 and all four --ch-*-trace. Which side was wrong was
   measured rather than assumed, by two instruments: this repo's converter, and
   headless Chrome painting each value into a canvas and reading the pixel back. They
   agree to the byte, controls included, and the comments were the stale half.        */

  --graphite-900: oklch(0.155 0.006 262);   /* #0B0C0F  the room: aperture surround */
  --graphite-800: oklch(0.205 0.008 262);   /* #15171B  the bench: app ground       */
  --graphite-700: oklch(0.248 0.009 262);   /* #1F2126  a pane                      */
  --graphite-600: oklch(0.300 0.010 262);   /* #2B2E33  a raised key, the live row  */
  --graphite-500: oklch(0.372 0.010 262);   /* #3D4046  a solid rule                */
  --graphite-400: oklch(0.442 0.007 262);   /* #515357  THE MOUNT; a proposed edge  */

  --paper-400:    oklch(0.575 0.008  85);   /* #7B7974  struck-through, superseded  */
  --paper-300:    oklch(0.700 0.007  85);   /* #A09E9A  field labels                */
  --paper-200:    oklch(0.812 0.006  85);   /* #C3C1BD  secondary ink               */
  --paper-100:    oklch(0.905 0.006  85);   /* #E1DFDB  primary ink                 */
  --paper-000:    oklch(0.965 0.007  85);   /* #F6F3EE  the sheet                   */
  --paper-ink:    oklch(0.435 0.011  80);   /* #54514A  ink ON the sheet. Field
                                               Notes' body ink is #545049 and it is
                                               never black; black on paper over a
                                               screenshot reads as a UI chip.       */

/* THE FOUR CHANNELS. Identical L and C, four hues 70–90° apart: an oscilloscope's
   traces are equally bright by construction so no channel outranks another by
   accident. C = 0.14 is the largest chroma at which all four stay inside sRGB, so
   no hue silently shifts on clipping. Each channel is a LOCKED PAIR — a trace and
   a cell of the same hue — and the pair is used exactly twice: ink on chrome, and
   ink on paper. There is no third rung and no tint ramp.                          */

  --ch-1-trace:  oklch(0.780 0.140  75);    /* #EBA941  amber   */
  --ch-1-print:  oklch(0.500 0.135  75);    /* #8F5400          */
  --ch-1-cell:   oklch(0.255 0.050  75);    /* #311F03          */

  --ch-2-trace:  oklch(0.780 0.140 150);    /* #6FD087  green   */
  --ch-2-print:  oklch(0.500 0.135 150);    /* #087736          */
  --ch-2-cell:   oklch(0.255 0.050 150);    /* #0E2915          */

  --ch-3-trace:  oklch(0.780 0.140 220);    /* #12CBF5  cyan    */
  --ch-3-print:  oklch(0.500 0.135 220);    /* #007298          */
  --ch-3-cell:   oklch(0.255 0.050 220);    /* #002833          */

  --ch-4-trace:  oklch(0.780 0.140 340);    /* #F292D3  magenta */
  --ch-4-print:  oklch(0.500 0.135 340);    /* #933F7B          */
  --ch-4-cell:   oklch(0.255 0.050 340);    /* #32192A          */

/* THE WARNING REGISTER. Amber is caution, red is act — the two-level alert
   convention of every cockpit and control room. Red is louder than any channel
   (higher chroma, lower lightness) so it cannot be mistaken for one, and it is
   spent on exactly one meaning: THIS NEEDS YOUR DECISION / THIS ANCHOR FAILED.   */

/* 🔴 THE TRACE MOVED, 2026-08-28, AND IT IS THE HALF OF A PATCH THAT NEVER LANDED.
   #F45249 measures 3.99:1 on --bg-raised (#2B2E33), which is under AA for the 12px
   field it is set in. `app/Theme.swift` derived the fix, wrote the patch into its
   own header for whoever owned this file, and then LEFT ITS OWN COPY at #F45249 on
   purpose, saying so: "patching the Swift copy while tokens.css still says #F45249
   is a local compensation — the two would disagree silently."

   ⚠️ SOMEBODY APPLIED IT TO THE SWIFT AND NOT TO THIS FILE, AND DID NOT UPDATE THE
   COMMENT THAT SAID THEY HADN'T. So `Theme.swift:172` has read #F5665E while
   `Theme.swift:37` two lines above still said it was deliberately left at #F45249.
   The showcase reads THIS file at run time and renders the alarm 23 times, so for
   four days the red on the public design sheet was the one that fails AA and the
   red inside the app was the one that passes. CTL·ALIGN-ALL row 170.

   MEASURED, on the three grounds this system draws on, both values:
       --bg-raised  #2B2E33     3.99  →  4.51   FAIL → PASS
       --bg-app     #15171B     4.68  →  5.28
       --bg-well    #0B0C0F     5.21  →  5.88
   ⚠️ 4.51 CLEARS 4.5 BY NOTHING. If --bg-raised ever darkens this fails again. It
   is a value that passes, not one with room, and that is written here rather than
   in one language's copy of it.

   ⭐ AND IT IS NOT THE SAME HUE AT A NEW LIGHTNESS, WHICH IS WHAT BOTH FILES SAID.
   Read back off the hex: #F45249 is oklch(0.660 0.200 27.2) and #F5665E is
   oklch(0.688 0.178 26.0). The CHROMA dropped too — 0.200 to 0.178 — which is what
   raising lightness in sRGB costs at this hue, and no amount of prose about
   "holding saturation exactly" changes the pixel. The number is what shipped and
   this is what it actually is.

   --alarm-print and --alarm-cell DO NOT MOVE. Print is measured on paper and the
   cell is a fill, neither is the 12px field that failed, and moving three values
   to fix one is how a palette drifts. */
  --alarm-trace: oklch(0.688 0.178  26);    /* #F5665E */
  --alarm-print: oklch(0.500 0.175  27);    /* #B12725 */
  --alarm-cell:  oklch(0.255 0.065  27);    /* #3D1411 */

}

/* TYPE. Four rungs, and hierarchy is NOT carried by them — it is carried by
   weight, by rules and by colour-as-identity. A printed parts catalogue sets a
   section head at 16 against 14 body and lets a horizontal rule do the rest.

   THE FACES ARE SF Pro, MD IO and MD Nichrome Bold: the words, the values and
   the labels, the same three the app draws through app/Theme.swift (controller calls
   36 to 42 of 2026-09-28, decided blind in docs/design/type-faceoff/). Brand v3 had
   moved the values to SF Mono and the labels to DIN Alternate for the trial licence
   alone, and he ruled that a licence is never a reason to change a face.

   Rejected with a measured reason, recorded so nobody re-proposes them:
     Apfel Grotezk       feature set is aalt frac kern liga locl ordn sups — no
                         tnum, no lnum, no zero, and default figures are
                         aggressively proportional (1 at 288 units against 0 at
                         632). A timecode column would shudder line to line and
                         it cannot be fixed from outside the file.
     Lab Grotesque Mono  Regular + Bold only. One timestamp could not be
                         emphasised against its neighbours.

   ONE DISCIPLINE, AND IT IS ENFORCED IN triage.css: monospace is reserved for
   data compared character by character — timecodes, coordinates, identifiers,
   deltas. Prose stays in the reading face, even short prose. Mono is a
   first-class text face here, not a code-block afterthought.                  */

/* Controller calls 36 to 42 (docs/rulings/2026-09-28-controller-calls.md): words SF Pro, labels MD Nichrome Bold in capitals, values MD IO; the files stay out of git and app/build.sh copies exactly these. */
@font-face{font-family:"MD Nichrome"; src:url("../../brand/fonts/mass-driver-trial/MD%20Nichrome/MDNichromeTrial-Bold.otf") format("opentype");
  font-weight:700; font-style:normal; font-display:block}
@font-face{font-family:"MD IO"; src:url("../../brand/fonts/mass-driver-trial/MD%20IO/MDIOTrial-Regular.otf") format("opentype");
  font-weight:400; font-style:normal; font-display:block}
@font-face{font-family:"MD IO"; src:url("../../brand/fonts/mass-driver-trial/MD%20IO/MDIOTrial-Medium.otf") format("opentype");
  font-weight:500; font-style:normal; font-display:block}
@font-face{font-family:"MD IO"; src:url("../../brand/fonts/mass-driver-trial/MD%20IO/MDIOTrial-Semibold.otf") format("opentype");
  font-weight:600; font-style:normal; font-display:block}
@font-face{font-family:"MD IO"; src:url("../../brand/fonts/mass-driver-trial/MD%20IO/MDIOTrial-Bold.otf") format("opentype");
  font-weight:700; font-style:normal; font-display:block}

:root{
  /* --font-ui, --font-mono, --font-display: section 4's --face-* are the faces; are aliases now, in the block at the end of this file. */
}
:root{

  /* 🔴 A RUNG SETS THE SIZE. THE CONTENT SETS THE FACE. These four sentences are
     DESIGN-LANGUAGE.md §2's ladder, word for word, and `TheTypeLadderHasOneOwner` in
     tests/test_the_words_have_one_owner.py holds them to it. The rule they replaced said `--type-field` is ALWAYS mono while the
     mono discipline said prose keeps the reading face however short, so a sentence at 12
     could obey neither and every sentence too small for 14 went to Menlo. Ten of twenty
     rendered sentences, across five app windows, measured at 1×. */
  /* --type-* and --lead-*: section 4's --text-* steps are the type; are aliases now, in the block at the end of this file. */


  --weight-text: 400;
  --weight-mid:  500;
  --weight-key:  700;
  --track-mark:  0.11em;   /* 2026-09-28, marks-hatch: MD Nichrome Bold's capitals are narrower than DIN's, so the 0.13em tuned on DIN opened them by 27.4% of a capital's advance against DIN's 22.5% and read loose. 0.11em is 23.2%. DESIGN-LANGUAGE.md section 2, Tracking */
  --track-head: -0.006em;

/* SPACE. The register is a ruled book. The ruled line is 20px — the body line box,
   14px at 1.4286 — and every step in the ladder is a whole number of TENTHS of it.
   Two full lines is the largest step there is; nothing in an instrument needs
   more air than that.                                                            */

  /* --sp-*: section 4's --space-* is the scale; are aliases now, in the block at the end of this file. */

  /* ── 🔴 ROW 660, RULED W94 · THE MEASURE, AND IT IS A COUNT OF CHARACTERS ────
     Five spellings across five files in three units, and a sixth statement as a
     grading threshold:

       site/site.css:305        34rem                    the prose column
       showcase/showcase.css    calc(var(--sp-20) * 12)  the prose column
       tools/ui/triage.css:37   56ch                     the register row
       tools/ui/triage.css:38   44ch                     the sticky and the stage
       app/Settle.swift         64ch off font advances   the evidence column
       showcase/_sizes.mjs:206  75ch                     what GRADES all of them

     The pick is a UNIT and the thing that decides it is the grader. `_sizes.mjs`
     measures every surface against 75 CHARACTERS, so a measure declared in px or
     rem cannot be read by the instrument that judges it without a conversion
     nobody has written. A measure is a count of characters by definition, and the
     majority, the owner and the grader all already say so.

     🔴 THE COUNT IS 50ch, WHICH SETS ABOUT 66 CHARACTERS A LINE IN SF Pro (T6b,
     2026-09-26). 66 is DESIGN-LANGUAGE's measure and `Face.measure` in app/Theme.swift.
     CSS `ch` is the advance of `0`, and a proportional face sets more characters than
     that per `ch`, because its lowercase is narrower than its figures. The ratio
     belongs to the face. This was 61ch, derived in Helvetica Neue, where it read
     longest 76 on the site. Ruling 8 moved the reading face to SF Pro, whose `0` is
     0.63em at text sizes, and the same `ch` set more: triage's 56ch went from 71
     characters a line to 76. On the render SF Pro holds about 1.33 characters per
     `ch`, so 66 characters is 50ch. The counts before and after, read off headless
     renders of the document and the home page, are in docs/design/audit/lanes/T6b.md.

     ⭐ ONE COUNT, READ BY THREE FILES' PROSE. The site's --col-text and its `.sub`
     paragraphs and triage.css's --measure-read all read this token, because all three
     set running prose in the same face at the same count, and `ch` resolves at each
     element's own size. triage.css's --measure-stage stays its own, shorter value on
     purpose, and app/Settle.swift's 64 is the evidence column. Re-run
     `python3 site/measure.py` and `node showcase/_sizes.mjs` after any edit, because
     both read the render. */
  --measure: 50ch;

/* FORM. Radius encodes material. A drawing has square corners; a key you press
   does not. The larger value is for a thing that floats over the work, and it is
   not a taste: --radius-window is the corner macOS gives a panel over the desktop.
   Brand v3 retired the note card, and the 10px step it held between the key and
   the window went with it.                                                      */

  --radius-draw:   0;
  --radius-key:    3px;
  --radius-window: 12px;

  --rule: 1px;     /* every rule in this interface is one pixel. There is no 2px rule. */

/* MOTION. The decide loop — select, keep, leave out, tag — has a hard budget of 100ms
   for seek plus repaint, and it is driven hundreds of times per session. It gets
   ZERO duration: not a fast transition, none. Two durations exist in the whole
   language and one of them is 0.                                                */

  /* --dur-still, --dur-tick, --dur-settle, --ease-settle: section 5 holds motion; are aliases now, in the block at the end of this file. */


/* ─── 2 · SEMANTIC ──────────────────────────────────────────────────────────── */

  /* SURFACES, AND THEY ARE ONE STACK RATHER THAN A LIST. Ground, then three
     rungs above it. Read bottom-up:

     🔴 --bg-well IS THE GROUND OF EVERY FULL SURFACE. The site, the showcase, and
     every window this app opens — Guide:152, Receipt:49, Settle:897, Figma:187,
     Decide:568 all set `Ink.well`. CTL·ALIGN-ALL row 171: the showcase sat one
     rung lighter than the other ten panels on the contact sheet and it was the
     most visible difference on it. Not a choice — it links `triage.css`, which
     dresses an app WINDOW and sets --bg-app on the body, and nothing overrode it.

     ⚠️ --bg-app IS NOT "THE GROUND THE APP SITS ON", WHICH IS WHAT ITS NAME READS
     AS AND IS HOW THE ROW HAPPENED. It is the first rung ABOVE the ground: a
     block raised off the well but not a pane and not a key. `Decide.swift:692`
     backs the quote with it, `showcase.css` uses it for a specimen well.
     The name is filed for renaming as row 226 rather than renamed here, because a
     rename is 17 call sites across two languages and a test fixture and it moves
     no pixel. What moves a pixel is written above it. */
  --bg-well:     var(--graphite-900);   /* THE GROUND. every full surface        */
  --bg-app:      var(--graphite-800);   /* a block raised off the ground         */
  --bg-pane:     var(--graphite-700);   /* a pane inside a window                */
  --bg-raised:   var(--graphite-600);   /* a raised key, the live row            */
  --bg-sheet:    var(--paper-000);      /* a note written over the evidence      */

  /* THE MOUNT. ITU-R BT.2035 and ISO 3664 both specify the surround for judging
     an image as a neutral grey at roughly 10% of peak white. --paper-000 is our
     peak white; --graphite-400 is 10% of its luminance. This is the one band of
     the chrome that touches the evidence, and it is a measured value, not taste.

     🔴 WHERE IT MAY BE DRAWN IS §7'S, AND THIS FILE USED TO ARGUE IT AGAIN.
     Row 197 was four documents holding three positions on the mount. §7 settled it
     on 2026-08-28 and this block went on restating the whole argument in its own
     words, which is a second copy of a decision and the way the third position gets
     written. **The rule is one sentence in DESIGN-LANGUAGE §7, under "Considered and
     cut", and it is not repeated here.** Read it before spending this token.

     ⚠️ AND IT IS THE MAT BAND, NOTHING ELSE. `triage.css` used to spend this token
     on a dashed proposal border, reasoning that "--surface-mount is already the
     language's dashed rule". Two meanings under one name is how --ink-dead ended
     up as a document's rule ink. The dashed edge is --rule-proposed below; both
     read --graphite-400, which is one rung with two jobs and now two names. */
  --surface-mount: var(--graphite-400);

  /* ink */
  /* 🔴 THE RUNG ABOVE --ink, AND IT DID NOT EXIST — CTL·ALIGN-ALL row 172. The ink
     ladder stopped at --paper-100, so `site.css` reached PAST the semantic layer
     into the primitive and set 12 of its 42 text leaves in --paper-000, which this
     file declares as --bg-sheet: a GROUND. A stylesheet spending a ground token as
     ink is the layering this file's own header forbids in its first ten lines.

     THE SITE WAS RIGHT ABOUT THE PIXEL AND THE LADDER WAS SHORT. Its reasoning is
     worth keeping: at 16px, --ink against --ink-2 is a difference you can MEASURE
     and cannot SEE, and hierarchy that is not carried by size has to be carried
     somewhere. --paper-000 is the visible step.

     ⚠️ IT IS AN ACCENT AND THE RULE IS THE SAME ONE --signal HAS: spent on the one
     thing that most deserves it, never as a default. On the site that is four
     objects — the quote a person actually said, the answer carrying the measured
     number, a register name, and the requirement list. The app has not spent it at
     all, which is a surface deciding it has nothing that loud, not a gap. */
  --ink-bright:  var(--paper-000);      /* the brightest ink. AN ACCENT. */
  --ink:         var(--paper-100);
  --ink-2:       var(--paper-200);
  --ink-label:   var(--paper-300);
  --ink-dead:    var(--paper-400);      /* superseded, never deleted */
  --ink-sheet:   var(--paper-ink);      /* ink on --bg-sheet */

  /* rules. Solid = section. Dashed = subsection. The distinction carries meaning
     and is not decorative, so the two colours differ as well as the two styles. */
  --rule-section: var(--graphite-500);
  --rule-sub:     var(--graphite-500);   /* same ink as a section rule — the STYLE
                                            carries the distinction, not the colour,
                                            and a subsection rule you cannot see at
                                            1× is not a rule */
  --rule-hatch:   var(--paper-400);     /* the 45° fill of a null / superseded cell, and of a left-out bullet. It carries a state, so it clears 3:1 (WCAG 1.4.11): 3.70 on the dark pane, 4.13 on the ground. No graphite step clears 3:1 on graphite; graphite-400 reads 2.09 */
  /* 🔴 THE SECOND RULE OF THE PAIR, ROW 196. A specimen is a screenshot of somebody
     else's website, so one side of its edge is a fill nobody controls. Measured by
     `docs/decisions/the-mount/contrast.py`: --rule-section is 9.12:1 on the light
     capture this repo happens to hold and 1.25:1 on a dark one, 1.80:1 on a
     near-black one, against 1.88:1 on the well it always has behind it. On a dark
     page the evidence has no boundary at all. So a specimen's edge is drawn twice,
     the dark rule inside and this one outside, and whichever side the fill lands on,
     one of the two clears it: --rule-light is 7.31:1 on the well, 4.87:1 on the dark
     capture and 7.01:1 on the near-black one.

     ⚠️ IT IS A RULE INK AND NOT AN INK RUNG, WHICH IS ROW 173 ONE PARAGRAPH DOWN.
     The value is --paper-300's and the meaning is not: --ink-label is the ink of a
     field label, this is the ink of a stroke. White at 0.9 measures 15.82 and 10.53
     and is louder than anything else on these surfaces, so it was not taken.
     §6 already ruled this for the 16px locator ring. This is the same rule at the
     scale of the rectangle around it. */
  --rule-light:   var(--paper-300);
  /* 🔴 THE RULE ON PAPER, AND IT DID NOT EXIST — CTL·ALIGN-ALL row 173. Every rule
     in `competitor.docx`, the thing that actually leaves this machine, was drawn in
     --ink-dead. That token means SUPERSEDED, NEVER DELETED: it is the ink of a
     struck-through observation and nothing else. `tools/export/sheet.py` needed a
     rule ink for a white page, found no `-print` rung for one, and spent the
     nearest thing — so the document's own grammar said "every row of this table is
     cancelled".

     The VALUE was right and the NAME was not, which is why nothing looked wrong.
     --paper-400 on white is a 4.11:1 hairline, quiet and visible, exactly what a
     0.5pt rule wants. One rung, two names, the same shape as --surface-mount and
     --rule-proposed above: a token is a meaning, and two meanings may not share
     one. */
  --rule-print:   var(--paper-400);
  /* 🔴 A DASHED EDGE IS A PROPOSAL, A FILLED ONE IS A DECISION — RUNTIME.md §2 in
     one border-style. It is one rung lighter than a rule because it is not one: it
     is the edge of a control that is not at rest, which is the recommended key
     before anybody presses it and any key under the pointer. It was drawn out of
     --surface-mount, and a token spent on two meanings is the defect row 173 filed
     against --ink-dead. Same ink, its own name. */
  --rule-proposed: var(--graphite-400);

  /* THE WIRE. One hue per observation, carried identically through the register
     row, the mark on the frame and the leader — and spent on nothing else. The
     tag names are the contract with triage.js, which writes them as class names
     and, in one place, as `var(--weakness)` inline. */
  --strength:      var(--ch-2-trace);   --strength-cell:    var(--ch-2-cell);   --strength-print:    var(--ch-2-print);
  --weakness:      var(--ch-1-trace);   --weakness-cell:    var(--ch-1-cell);   --weakness-print:    var(--ch-1-print);
  --opportunity:   var(--ch-3-trace);   --opportunity-cell: var(--ch-3-cell);   --opportunity-print: var(--ch-3-print);
  --question:      var(--ch-4-trace);   --question-cell:    var(--ch-4-cell);   --question-print:    var(--ch-4-print);

  /* the reserved signal */
  --signal:        var(--alarm-trace);
  --signal-cell:   var(--alarm-cell);

  --focus: var(--paper-000);            /* non-chromatic, so a focus ring never
                                           borrows a channel's meaning */


/* ─── 3 · COMPONENT ─────────────────────────────────────────────────────────── */

  --plate-h:        44px;    /* the title block: two ruled lines plus its rule */
  /* 🔴 44 HERE AND 56 IN THE APP WAS `CTL·ALIGN-ALL` ROW 186 — a 27% difference on the
     one band every surface has, and the first thing the eye hits. It reads as two
     surfaces disagreeing and it is not: triage and settle carry the session ADDRESS
     under the sheet name and the web plate carries no address at all. One extra rung,
     one extra interval. So the app derives `44 + 12` from `Plate.band` rather than
     typing 56, and this line is still the only place the 44 is chosen.
     ⚠️ If this moves, `app/Theme.swift`'s `Plate` moves with it. Both, or neither.

     🔴 ROW 187 GAVE THE 44 AND THE 56 THEIR RULE, AND THE RULE IS A MECHANISM.
     Does this surface act on ONE NAMED THING? Triage and settle judge one session,
     so their plate carries that session's address under the sheet name and takes the
     56. The site, the showcase, the guide and the setup panel are documents with
     nothing to address, so they take this 44: one line, name left, and on the right
     either a role or a readout, never both. Two kinds, and no surface gets to invent
     a third. A layout that forces a page to grow an address it has not got is the
     option row 187 drew and threw out. */
  --pane-register: 344px;    /* wide enough for a 14px sentence to run two lines, not five */
  --pane-decide:   300px;    /* the anchor record is a narrow table; it does not want more */
  /* THE FLOORS THE SIDE PANES GIVE WAY TO. The evidence is the one surface that
     must not be the thing that shrinks: with three fixed tracks it fell to
     378×315 in a 378×614 room at 1024 — 48.7% of it black. The side panes are
     clamped between these floors and the widths above, so they hold their
     designed size where there is room and hand it back where there is not.
     224px still fits a 14px sentence on two lines; 208px still fits the anchor
     record's widest row (`SAID 0:03.2–0:05.2`). */
  --pane-register-min: 14rem;   /* 224px */
  --pane-decide-min:   13rem;   /* 208px */
  --register-gutter: 26px;   /* the part-number column of the register */
  --mount-band:     10px;    /* how much mount shows around the evidence */
  /* ROW 185 SAID THIS IS OFF THE `--sp-*` LADDER (2 4 8 12 16 20 28 40), AND IT IS —
     but §3 settled on 2026-08-28 what that ladder measures: "THE LADDER IS THE TYPED
     CONSTANT, AND IT IS MEASURED BETWEEN TEXT BOXES", baseline to cap height. A mat
     band is padding around an IMAGE. It is not an interval between two text boxes and
     the ladder does not reach it, the same way `--rule` at 1px is not on it either.
     ⚠️ THAT DISPOSES OF THE ROW AND NOT OF THE QUESTION. The SURROUND COLOUR is derived
     — ITU-R BT.2035 and ISO 3664, neutral grey at ~10% of peak white, argued in full at
     `--surface-mount` above. **The 10px is not derived from anything anybody wrote
     down.** Not invented a reason for it here: a value with a made-up justification is
     worse than a value with none, because the next reader stops asking.

     ✅ ROW 645 IS CLOSED AND THAT PARAGRAPH IS THE CLOSURE, WITH ITS MEASUREMENT.
     `W94·TRIGGERS`. The row asked for a reason and the honest reason is that there is
     none to find: **W47 rendered this band at 8, 10 and 12 at 1x and could not tell
     the three apart**, so no measurement separates them and nothing is waiting on
     anybody's eye. A value nobody can distinguish from its neighbours has no
     derivation to write, and the sentence above already says what to do about that.
     What was missing was the measurement standing beside it. Derived, not picked: the
     rule is this comment's own and the number behind it is W47's render. */
  --mark-dia:       16px;    /* the unfilled locator ring over the frame */
  --mark-dot:        4px;    /* the filled terminator: a coordinate on a surface */
  --sheet-max:     420px;    /* the legend is a reference card, not a dialog */
  --note-max:      230px;    /* a note over the frame carries a sentence, not a paragraph */
  --key-pad-y:       6px;
  --key-pad-x:      10px;
  --hit-min:        24px;    /* WCAG 2.2 · 2.5.8 target size (AA). Every pressable
                                thing in the interface clears it; it is a token so
                                the floor is stated once rather than re-typed. */
}

@media (prefers-reduced-motion: reduce) {
  :root { --dur-tick: var(--dur-state); --dur-settle: var(--dur-exit); }  /* fades keep their time; see the block in section 5 */
}

/* A FINGER IS NOT A CURSOR. 24px is the WCAG 2.2 AA floor and it is a floor, not
   a target: measured on a 390px viewport, `keep` and the tag keys cleared it and
   the help key came to 36px, which is still under every touch guideline written
   for a thumb. `hover:none` is the honest test for "there is no pointer here" —
   the same query this interface already uses to stand the key bindings down — so
   the floor rises with it. It is raised HERE, on the token, rather than on the
   eight controls that read it, which is the whole reason it is a token. */
@media (hover: none) {
  :root { --hit-min: 44px; }
}

/* ─── 4 · FOUNDATIONS, ROUND 1 (2026-09-23) ───────────────────────────────────
   Additive. Nothing above is renamed or revalued, so every surface renders as it did
   until a lane adopts these names. The evidence for each value is in
   docs/design/foundations/FOUNDATIONS.md, under the same heading as here.

   🔴 THE FOUR-RUNG LADDER IN LAYER 1 (--type-mark 10, --type-field 12, --type-body 14,
   --type-head 16) IS SUPERSEDED BY THE --text-* STEPS BELOW for the app and every
   surface that adopts them. §2 was rewritten to the sizes the rungs resolve to on
   2026-09-28, and `TheTypeLadderHasOneOwner` holds each rung's comment in the alias block
   at the end of this file to §2's sentence for it. Old to new: --type-mark to --text-label
   (MD Nichrome capitals, tracked --track-mark), --type-field to --text-mono, --type-body to --text-body,
   --type-head to --text-entry. --sp-* gives way to --space-* the same way. */
:root{
  /* TYPE B, entry led. Apple's Mac styles in SF Pro, values in MD IO, three sizes a window
     (17, 13, 11), mono at 12, nothing under 10. The system tracks SF by size, so no
     step sets letter-spacing. */
  --face-app:      -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
  --face-app-mono: "MD IO", ui-monospace, "SF Mono", monospace;
  /* MD Nichrome is loaded at Bold alone, so a label draws 700 whatever a rule asks (call 37) */
  --face-label:    "MD Nichrome", sans-serif;
  --text-entry:  600 17px/22px var(--face-app);   /* Title 2, semibold: the one first stop of a pane */
  --text-row:    500 13px/16px var(--face-app);   /* a list row's title */
  --text-body:   400 13px/18px var(--face-app);   /* Body, opened to 18 for sentences */
  --text-button: 500 13px/16px var(--face-app);   /* title case, never tracked caps */
  --text-label:  700 11px/14px var(--face-label); /* MD Nichrome Bold capitals, tracked --track-mark: a label */
  --text-meta:   400 11px/14px var(--face-app);   /* Subheadline: a row's second line */
  --text-mono:   400 12px/16px var(--face-app-mono); /* a value, a time, a coordinate */
  --text-floor:  10px;                            /* HIG minimum; no step goes under it */
  /* A step's parts, for a rule that sets one property: font-size or line-height alone.
     Each repeats the number inside its step above; tests/test_one_token_family.py fails
     the day they differ. */
  --text-entry-size:  17px; --text-entry-line:  22px;
  --text-row-size:    13px; --text-row-line:    16px;
  --text-body-size:   13px; --text-body-line:   18px;
  --text-button-size: 13px; --text-button-line: 16px;
  --text-label-size:  11px; --text-label-line:  14px;
  --text-meta-size:   11px; --text-meta-line:   14px;
  --text-mono-size:   12px; --text-mono-line:   16px;
  /* the site keeps Type A's heading and prose: B's 22 over 15 sat too close */
  --text-site-head: 600 26px/32px var(--face-app);  /* Large Title, semibold */
  --text-site-body: 400 17px/26px var(--face-app);  /* Title 2's size, set as prose */
  /* the site's display steps and its caption (docs/design/audit/V3-site.md, finding 7): the
     hero's claim and the page's one number, a section's claim, and the line under a picture
     or a button. The two display steps are fluid from a phone to 1440 and stop there */
  --text-site-hero:    600 clamp(40px, 4.45vw, 64px)/1.05 var(--face-app);  /* the v4 hero's headline alone: 64 at 1440, 40 on a phone */
  --text-site-display: 600 clamp(36px, 3.2vw, 48px)/1.1 var(--face-app);
  --text-site-title:   600 clamp(30px, 2.8vw, 40px)/1.2 var(--face-app);
  --text-site-note:    400 15px/22px var(--face-app);

  /* SPACE. A 4 pt scale. Every gap names a proximity tier; nothing else is a gap. */
  --space-1:   4px;
  --space-2:   8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-6:  24px;
  --space-8:  32px;
  --space-12: 48px;
  --space-18: 72px;
  --gap-related: var(--space-1);   /* a label to its value, a title to its meta line */
  --gap-group:   var(--space-4);   /* one block to the next inside a pane */
  --gap-section: var(--space-8);   /* pane to pane, section to section */

  /* DENSITY, regular: rows tight but separate, as in Things. */
  --row-pad-y:      var(--space-2);    /* 8 above and below a row's text */
  --row-gap:        2px;               /* between two rows, so two fills never touch */
  --pane-pad:       var(--space-4);    /* inside a pane */
  --site-first-pad: var(--space-12);   /* above the site's first block */
  --control-h:      28px;              /* Apple's default control, above --hit-min */
  /* a button, a row fill and a choice are keys, so they take --radius-key.
     Radius encodes material (layer 1, FORM). */

  /* THE ACCENT, non-chromatic. The four hues and the alarm already mean something,
     and a system blue sits 35° from the cyan that means opportunity. So the accent is
     the paper: --paper-000 on dark, --graphite-800 on light. Spent twice a window:
     solid on the one primary action, and as the selected row's edge. */
  --accent:     var(--paper-000);
  --accent-ink: var(--graphite-800);   /* text on the accent, 16.2:1 */

}

/* SOUND. The app owns no pitch, level or file: every sound it makes is the Mac's own alert,
   at the alert volume the person set in Sound settings, so there is no tier to name here.
   Two values, inventoried in docs/design/audit/lanes/tokens-two.md. Theme.swift's `Sound`
   reads them, and tools/ui/theme_swift.py stops on any keyword it has no Swift form for. */
:root{
  --sound-refuse: system-alert;   /* a key or a button pressed where it cannot act, NSSound.beep() */
  --sound-scrub:  muted;          /* the scrub pane's preview video: the mic track is in it, and the pane is not for listening */
}

/* THE SPINE, the left edge content hangs from. Two, and the content picks which one:
   docs/design/IDENTITY.md §Spine, row 189. Theme.swift's `Spine` reads them. */
:root{
  --spine-edge:  var(--space-4);  /* 16. A pane, a register or a control that meets the window edge */
  --spine-prose: var(--space-8);  /* 32. The gutter round one column of prose, the guide */
}
/* The accent and everything mixed from it are declared again on every element that
   sets an appearance or is the stage, because a custom property is resolved where it is
   declared: mixed once on :root, a light window would inherit dark hovers. */
:root, [data-appearance], [data-stage]{
  /* STATES. One of each per control kind, in CSS and in Swift. Hover is a faint fill,
     selected a stronger fill plus the accent edge, so the two cannot read alike: the
     round-1 readers took 5% and 12% for two selected rows. Focus is a ring and never a
     fill, so focus and selected never share a drawing. */
  --state-hover:     color-mix(in oklab, var(--accent)  4%, transparent);
  --state-pressed:   color-mix(in oklab, var(--accent) 10%, transparent);
  --state-selected:  color-mix(in oklab, var(--accent) 14%, transparent);
  --state-edge:      2px;                      /* the selected row's leading edge, in --accent */
  --state-focus:     var(--accent);            /* a 2px ring, offset 2px, never a fill */
  --state-focus-w:   2px;
  --state-disabled:  0.4;                      /* opacity of a disabled control's whole drawing */

  /* per kind. A row and a choice select; a button presses; the primary fills. */
  --button-fill:           transparent;
  --button-edge:           var(--app-rule);
  --button-hover:          var(--state-hover);
  --button-pressed:        var(--state-pressed);
  --primary-fill:          var(--accent);
  --primary-ink:           var(--accent-ink);
  --primary-hover:         color-mix(in oklab, var(--accent) 90%, var(--accent-ink));
  --primary-pressed:       color-mix(in oklab, var(--accent) 80%, var(--accent-ink));
  --row-hover:             var(--state-hover);
  --row-pressed:           var(--state-pressed);
  --row-selected:          var(--state-selected);
  --row-selected-edge:     var(--accent);
  --row-selected-inactive: color-mix(in oklab, var(--accent) 8%, transparent); /* window not key: the fill dims, the edge goes */
  --choice-hover:          var(--state-hover);
  --choice-selected:       var(--state-selected);
  --choice-selected-edge:  var(--accent);      /* a 1px full border on the chosen option */
}

/* ─── 4b · APPEARANCE. Every window follows the system; only the stage stays dark ──
   The decision and its evidence: FOUNDATIONS.md, "Light mode". A surface opts in with
   data-appearance="system" (follows prefers-color-scheme) or "light" / "dark" (a
   render forcing one). Nothing that has not opted in changes, because the --app-*
   names are new and default to today's dark values.
   Swift: labelColor, secondaryLabelColor, tertiaryLabelColor, separatorColor,
   windowBackgroundColor, controlBackgroundColor; the stage alone keeps a fixed
   NSColor from --stage-surround and draws with NSAppearance(named: .darkAqua). */
:root, [data-appearance="dark"]{
  --app-ground:     var(--graphite-800);  /* a window's background        */
  --app-pane:       var(--graphite-700);  /* the list and the inspector   */
  --app-ink-bright: var(--paper-000);     /* the entry                    */
  --app-ink:        var(--paper-100);
  --app-ink-2:      var(--paper-200);
  --app-ink-label:  var(--paper-300);     /* 6.0:1 on --app-pane          */
  --app-rule:       var(--graphite-500);
  /* The rest of the app's inks, one per Swift Ink, so app/Theme.swift is generated
     from this file (tools/ui/theme_swift.py) and never typed. Each light value
     below is derived in the light blocks, with its ratio. */
  --app-well:          var(--graphite-900);  /* the room: the register's ground   */
  --app-plate:         var(--graphite-600);  /* a raised key, the live row        */
  --app-ink-dead:      var(--paper-400);     /* superseded, never "quiet"         */
  --app-rule-proposed: var(--graphite-400);  /* a dashed edge, a hovered key      */
  --app-signal:        var(--alarm-trace);   /* the reserved red                  */
  --app-on-fill:       var(--graphite-900);  /* a letter on a tag fill: 9.15:1 at worst, on --ch-4-trace */
  /* THE STAGE. The pane that frames somebody else's site, in triage and settle.
     C3's reason holds here and nowhere else: a neutral dark ground does not bias a
     picture being judged. Fixed in both appearances, and so is everything drawn
     on it: the scrub rail under the picture, its four tag cells, and the second
     rule that edges a specimen. None of these has a light twin, on purpose. */
  --stage-surround: var(--graphite-900);
  --stage-mount:    var(--surface-mount);
  --stage-rule-light:       var(--rule-light);
  --stage-strength-cell:    var(--strength-cell);
  --stage-weakness-cell:    var(--weakness-cell);
  --stage-opportunity-cell: var(--opportunity-cell);
  --stage-question-cell:    var(--question-cell);
  /* a note's colour drawn in a pane. The trace is tuned for a dark ground; on paper
     the same hue is drawn with its -print rung, which exists for ink on paper. Hue and
     meaning are unchanged (F6). */
  --app-strength: var(--strength);  --app-weakness: var(--weakness);
  --app-opportunity: var(--opportunity);  --app-question: var(--question);
  /* The dark accent, here as well as on :root, so a dark panel inside a light page draws its primary
     in paper instead of inheriting the light page's graphite (the showcase's Dark panels did). */
  --accent: var(--paper-000);  --accent-ink: var(--graphite-800);
}
[data-appearance], [data-stage]{
  --focus-ring: oklch(0.625 0.206 255.5);   /* #0A84FF  keyboard focus in an app window, as macOS draws it (NSColor.keyboardFocusIndicatorColor); never a mark, never a fill (ruling 19). --focus stays the paper fill it always was */
}
:root[data-appearance="light"], [data-appearance="light"]{
  --app-ground:     oklch(0.940 0.014 80);   /* warm paper, the light twin of the bench; stone, ruling 27a, #F0EAE1 */
  --app-pane:       oklch(0.975 0.016 80);   /* #FDF6EB */
  --app-ink-bright: var(--graphite-900);
  --app-ink:        var(--graphite-800);     /* 15.0:1 on --app-ground */
  --app-ink-2:      var(--graphite-500);     /* 8.7:1  */
  --app-ink-label:  var(--graphite-400);     /* 5.39:1 on the live row, 7.18 on the pane, 6.45 on the ground */
  --app-rule:       oklch(0.855 0.017 80);   /* #D5CEC3, 0.085 L under the ground */
  --app-well:          var(--app-pane);       /* the register is the pane in light */
  --app-plate:         oklch(0.881 0.014 78.3);   /* #DDD7CE, the live row and a key: graphite-800 at 14% over the pane, painted opaque so a key's red label reads 4.61:1 on any ground */
  --app-ink-dead:      var(--paper-400);      /* 4.05 on the pane, 3.64 on the ground; dark reads 4.50 and 4.13 */
  --app-rule-proposed: var(--paper-300);      /* 2.49 on the pane, 2.24 on the ground; dark reads 2.54 and 2.33 */
  --rule-hatch:        var(--paper-400);      /* 4.05 on the pane, 3.64 on the ground: the lightest neutral that clears 3:1, so the hatch stays a texture. graphite-400 would read 7.18 */
  --app-signal:        var(--alarm-print);    /* 6.14 on the pane, 5.51 on the ground, 4.61 on the live row */
  --app-on-fill:       var(--app-pane);       /* 5.07:1 at worst, on --ch-3-print */
  --accent:         var(--graphite-800);     /* graphite on light */
  --accent-ink:     var(--paper-000);
  --focus-ring:     oklch(0.603 0.218 257.4);  /* the light twin of the system blue */
  --app-strength: var(--strength-print);  --app-weakness: var(--weakness-print);
  --app-opportunity: var(--opportunity-print);  --app-question: var(--question-print);
}
@media (prefers-color-scheme: light){
  [data-appearance="system"]{
    --app-ground:     oklch(0.940 0.014 80);
    --app-pane:       oklch(0.975 0.016 80);   /* #FDF6EB */
    --app-ink-bright: var(--graphite-900);
    --app-ink:        var(--graphite-800);
    --app-ink-2:      var(--graphite-500);
    --app-ink-label:  var(--graphite-400);
    --app-rule:       oklch(0.855 0.017 80);   /* #D5CEC3, 0.085 L under the ground */
    --app-well:          var(--app-pane);
    --app-plate:         oklch(0.881 0.014 78.3);   /* #DDD7CE */
    --app-ink-dead:      var(--paper-400);
    --app-rule-proposed: var(--paper-300);
    --rule-hatch:        var(--paper-400);
    --app-signal:        var(--alarm-print);
    --app-on-fill:       var(--app-pane);
    --accent:         var(--graphite-800);
    --accent-ink:     var(--paper-000);
    --focus-ring:     oklch(0.603 0.218 257.4);  /* the light twin of the system blue */
    --app-strength: var(--strength-print);  --app-weakness: var(--weakness-print);
    --app-opportunity: var(--opportunity-print);  --app-question: var(--question-print);
    }
}
/* The stage re-declares the dark accent, so a ring or a primary drawn on it stays
   paper even in a light window. */
[data-stage]{ --accent: var(--paper-000); --accent-ink: var(--graphite-800); --focus-ring: oklch(0.625 0.206 255.5); }

/* ─── 5 · MOTION, FOUNDATIONS ROUND 3 (2026-09-23) ─────────────────────────────
   Additive. --dur-still, --dur-tick, --dur-settle and --ease-settle above keep their
   values. The decide loop still reads --dur-still and never animates. These are for
   the rare moments only: a recording opening, triage to settle, a note arriving on the
   board, the receipt, done. Evidence and storyboards: docs/design/foundations/MOTION.md.

   Four durations, one per kind of change:
     tick    70ms  a key acknowledging a press (above, unchanged)
     state  120ms  a colour or fill changing under the pointer: hover, pressed, selected
     view   200ms  a pane changing what it shows. CleanShot X lands a dropped capture in
                   200ms, measured frame by frame.
     moment 320ms  a thing arriving that you will look at: a mark, a note, the receipt.
   Exits run at --dur-settle (140ms), faster than any entrance.

   Three names, two curves and no spring. Nothing here follows a finger, so nothing needs to keep a
   velocity (`to-spring-or-not-to-spring`), and none of the four products measured
   overshoots.
     enter     = --ease-settle. Half the travel is done at 20% of the time; Screen
                 Studio's camera measured 22% to 31%.
     standard  half-way at 50%, for a thing moving from one place to another on screen.
     exit      = --ease-settle as well. It was an ease-in, half-way at 65%, and the settle
                 sheet showed the old pane still near full strength 70 ms into a 140 ms
                 exit. An ease-out gets the leaving thing out of the way at once.

   Reduced motion: a colour change keeps its 120ms, since nothing moves. Every move goes to zero travel, zero scale change and zero stagger,
   and a line that draws itself is drawn already. What is left is an opacity fade over
   the same duration, so the moment still happens without anything moving.

   Swift, for the app lane (Motion.swift, reading
   NSWorkspace.shared.accessibilityDisplayShouldReduceMotion):
     Motion.state  0.12   Motion.view 0.20   Motion.moment 0.32   Motion.exit 0.14
     Motion.enter    CAMediaTimingFunction(controlPoints: 0.2, 0, 0, 1)
     Motion.standard CAMediaTimingFunction(controlPoints: 0.65, 0, 0.35, 1)
     Motion.exitCurve CAMediaTimingFunction(controlPoints: 0.2, 0, 0, 1)
     Motion.travel 8   Motion.scaleIn 0.96   Motion.stagger 0.04                        */
:root{
  --dur-state:  120ms;
  --dur-view:   200ms;
  --dur-moment: 320ms;
  --dur-exit:   140ms;
  --stagger:    40ms;      /* between notes landing; capped at 8 notes, the rest land together */

  --ease-enter:    cubic-bezier(0.2, 0, 0, 1);
  --ease-standard: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-exit:     var(--ease-enter);

  --motion-travel:   8px;  /* how far an arriving thing comes from */
  --motion-scale-in: 0.96; /* never from 0: a thing arrives nearly its own size */
  --motion-draw:     1;    /* share of a mark still undrawn when it starts */
}
@media (prefers-reduced-motion: reduce){
  :root{
    --stagger: 0ms;
    --motion-travel: 0px;
    --motion-scale-in: 1;
    --motion-draw: 0;
  }
}

/* ── ALIASES, FOR ONE RELEASE ──────────────────────────────────────────────────
   Section 1's type, space and motion names, each pointing at the nearest step of the one
   family (the 2026-09-23 ruling: section 4's and section 5's). site/site.css and the v2
   sheets still read them; each moves in its own lane, and this block goes when the last
   reader has. Old and new px for every row: docs/design/audit/lanes/tokens-one.md.
   An alias reads a step, never another alias, and none resolves through itself:
   tests/test_one_token_family.py holds both. */
:root{
  --font-ui:      var(--face-app);
  --font-mono:    var(--face-app-mono);
  --font-display: var(--face-label);

  --type-mark:  var(--text-label-size);  /* 11/14, labels in MD Nichrome Bold capitals, tracked --track-mark. A label names a plate, a section or a verdict on one line, and no sentence ever sits at this rung. */
  --type-field: var(--text-mono-size);   /* 12/16, values in MD IO. A file name, a size or a timecode sits here, where people match it against Finder or a scrubber one character at a time. */
  --type-body:  var(--text-body-size);   /* 13/18, words in SF Pro. Sentences, buttons and a row's title sit here, at the Mac's own body size. */
  --type-head:  var(--text-entry-size);  /* 17/22, a pane's first stop and its readout in SF Pro. A section head sits here, and so does the one number a window reports, set semibold in tabular figures. */
  --lead-mark:  1.2727;  /* was 1.      14/11, --text-label's line over its size. A ratio, so it inherits as one */
  --lead-field: 1.3333;  /* was 1.5.    16/12, --text-mono  */
  --lead-body:  1.3846;  /* was 1.4286. 18/13, --text-body  */
  --lead-head:  1.2941;  /* was 1.25.   22/17, --text-entry */

  --sp-1:  var(--space-1);  /* was 2. The scale has no 2 */
  --sp-2:  var(--space-1);
  --sp-4:  var(--space-2);
  --sp-6:  var(--space-3);
  --sp-8:  var(--space-4);
  --sp-10: var(--space-4);  /* was 20, the ruled line. 16 and 24 are equally near; the Swift theme took 16 */
  --sp-14: var(--space-6);  /* was 28 */
  --sp-20: var(--space-8);  /* was 40 */

  --dur-still:   0ms;              /* the one literal: the family has no zero step, and still needs none */
  --dur-tick:    var(--dur-state); /* was 70ms */
  --dur-settle:  var(--dur-exit);
  --ease-settle: var(--ease-enter);
}
