/* THE NATIVE-SHELL CHANNEL RULE (M-ruled 2026-09-01: DOUBLE-TAP ZOOM OFF in
   the shell only — "it is not a browser"; web keeps browser behaviour). The
   class is stamped by v2/native-shell.js on the capacitor: scheme alone —
   mechanism, never configuration — so this rule is unreachable on http(s).
   touch-action: manipulation kills double-tap zoom and KEEPS pinch.
   The shelf's copy lives in index.html's inline sheet (it links no css). */
html.native-shell body { touch-action: manipulation; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE ONE HOME OF THE RULED COLOUR SYSTEM (coherence audit phase 2, 2026-07-31).

   Every v2 document links THIS file for its theme tokens; none declares its own
   copy of anything declared here. Before this file the identical token blocks
   were COPY-PASTED across four documents — the mechanism behind the --dim
   spread (Dawn tuned once, propagated, never re-measured): a ruled value with
   no single home drifts BY CONSTRUCTION, no negligence required. This file is
   where "these must agree" is finally writable — by being the only place the
   values exist. tests/ui/theme-tokens-agree.mjs guards the sole-home property:
   a token re-declared in any v2 document is a SHADOW HOME and reds.

   WHAT DELIBERATELY DOES NOT LIVE HERE (page-local BY RULING, each with its
   reason, each listed in the pin's exception map — an exception without a
   reason is indistinguishable from an oversight):
     --meta-ink     shrine.html only — the under-art identity pair, M-ruled at
                    7.5:1 (#159); its constraint is the SHRINE's backdrop stack.
     --chrome-fade  read.html + shrine.html — reveal GEOMETRY (the scrim
                    overhang), not palette; theme-independent; owned by the
                    rooms whose chrome it fades.
     --veil-a       shrine.html #mid scope — the veil's strength knob, ruled
                    with the Shrine's ambient stack.
     --bad-fill     NOWHERE, since the cutover (2026-08-01) — see the rehomed
                    ruling below and its ⚠ cutover note: the token's only
                    declarations lived in v1's index.html and died with it;
                    zero error-GROUND rules exist anywhere under web/ until a
                    v2 surface adopts one (that adoption re-reads the ruling).

   (HISTORY: until the cutover, 2026-08-01, v1's index.html did NOT link this
   file — its palette was a frozen copy the pin kept COMPARING against this
   home, with two DECLARED divergences, Dusk --ok/--warn below; the pin's v1
   arm self-retired when v1 was deleted, exactly as M ruled 2026-07-31. The
   live /reader/index.html is the PROMOTED v2 shelf, and it links this file
   like every other v2 document.)

   ── REHOMED RULING #1: the --bad / --bad-fill SPLIT (M-ruled 2026-07-28) ────
   Rehomed 2026-07-31 because its only written home was v1's index.html — a
   file cutover deletes ("a ruling exists only where it's written", with a
   deadline). The ruling, preserved:
     "SPLIT THE TOKEN. Don't soften the raise and don't hold it." --bad is the
     error INK; --bad-fill is the error GROUND. One token doing both has
     CONTRADICTORY REQUIREMENTS BY CONSTRUCTION: raising it for legibility as
     ink necessarily lightens it as ground — not a majority to honour, but the
     tell that two properties were sharing one name.
     THE VALUES ARE DERIVED, NOT PICKED (the --meta-ink method): M ruled the
     PAIR — error ink on error fill must not read QUIETER than the body text it
     interrupts: |Lc(ink,fill)| >= |Lc(--tx, the ground #err covers)|. Each hex
     is the shipped seed scaled along its own linear-light ramp (chromaticity
     preserved) to the first 8-bit step clearing that floor by >= 1.0 Lc:
       Dawn seed #C4462F 78.6 vs floor 83.5 → #AD3D28 (84.5)
       Day  seed #B3402A 82.9 vs floor 90.8 → #8E311F (91.8)
       Dusk seed #A84A38 83.1 vs floor 78.1 → seed KEPT (+5.0; raise where
            short, never lower to the floor)
     Dusk DECLARES --bad-fill (its value differs from :root's) while --bad
     deliberately inherits — two tokens, two reasons, two shapes. The 16%/45%
     tints (.qbadge.q-fail, #libDown) ride --bad-fill because they are grounds.
     Values live in v1 (the only file with error-ground rules) until a v2
     surface adopts an error GROUND — that adoption re-reads THIS ruling and
     brings the derivation, not just the hexes. Pinned: error-fill-pair.mjs,
     error-ink-level.mjs (reads the RESOLVED cascade).

     ⚠ CUTOVER EXECUTED 2026-08-01 — AND THIS PARAGRAPH IS NOW THE RULING'S
     ONLY HOME, WHICH IS WHAT THE 07-31 REHOMING WAS FOR. v1's index.html is
     deleted. MEASURED on the cutover commit: `--bad-fill` has ZERO
     declarations and ZERO `var(--bad-fill)` consumers anywhere under web/ —
     the token is not "in v2 somewhere", it is GONE, because no v2 surface has
     yet adopted an error GROUND. The three derived hexes above (Dawn #AD3D28,
     Day #8E311F, Dusk seed KEPT #A84A38) survive HERE as a record and nowhere
     else in the tree.
     · error-fill-pair.mjs RETIRED in the same commit: its subject was the
       rendered ink-on-fill PAIR, and with no fill consumer left it reported
       "ZERO rules paint a banner fill token as a background … ZERO cells were
       gated. Everything was measured and excused, which is a green run that
       asserts nothing" — its own positive controls refusing to pass over an
       empty set, exactly as built.
     · error-ink-level.mjs SURVIVES and still gates the INK half (--bad at or
       above resting-text level, on the grounds the app really paints).
     THE STANDING INSTRUCTION IS UNCHANGED AND NOW LOAD-BEARING: the first v2
     surface to adopt an error GROUND re-reads this ruling, brings the
     DERIVATION (scale each shipped seed along its own linear-light ramp to the
     first 8-bit step clearing |Lc(--tx, the covered ground)| by >= 1.0), and
     restores a pair pin beside it. Do not copy the hexes without the method.

   ── REHOMED RULING #2: Dusk STATUS COLOURS INHERIT (--bad M-ruled 2026-07-28;
      --ok/--warn M-ruled 2026-07-31 BY THAT PRECEDENT, not by measurement) ───
   Dusk's grounds are DARKER than Dawn's, so Dawn's inks score HIGHER there:
   --bad measured |Lc| 84.1 inherited vs 23.5 damped — "inheritance WON; the
   deletion is the ruling." v1's night block still carries damped --ok #647F54
   and --warn #A8842C; v2 has never declared either. M ruled 2026-07-31: NO v2
   Dusk overrides — same token family, same theme, same question, already
   answered; re-deriving from painted numbers would relitigate a settled ruling
   in a different costume. The v1↔home difference is a DECLARED DIVERGENCE in
   the pin, not drift. Reopens only if painted proof shows v2's Dusk resolution
   materially wrong; the default is inheritance.

   ── REHOMED RULING #3: Dusk is FULL-CONTRAST, and the book ink is PAIRED ────
   (M's round-4 verdict, previously only in v1) "a dark UI, not a dimmed one" —
   #DCCFB6 on #0A0908 = 12.92:1, matching Day (12.92) and Dawn (12.98). PAIRED
   VALUE: the book-content ink injected into the reading iframe is a SEPARATE
   DOCUMENT and must stay identical to --tx per theme — a cross-file
   relationship no cascade can enforce; it belongs to the audit's arm-2 ledger.
   ⚠ POINTER CORRECTED 2026-08-06: this ruling named "v2 type-css.js" as the
   book-ink home. THAT FILE HAS NEVER EXISTED IN THE TREE (`git ls-files`
   returns nothing). The real home is **v2/console.js:282**, the
   BOOK_CSS_BY_THEME table — v1's reader.js BOOK_CSS_BY_THEME died at cutover.
   Coverage MEASURED, not assumed: tests/ui/theme-ink.mjs reaches it BY
   DERIVATION (themes from theme-boot.js, hosts from every *.html, injectors by
   `.setStyles(`) — it names no file, so the correction above does not move the
   guard. It also does NOT cover the theme SWATCH literals; see the note below.

   ⚠⚠ DAWN DEVIATES FROM THE ~12.9 PARITY, DELIBERATELY — M-ruled 2026-08-06,
   ✅ CONFIRMED ON DEVICE THE SAME DAY (the provisional marker that stood here is
   DISCHARGED, not deleted, because the sequence is the point: the value was
   chosen by measurement, held by ruling, and CONFIRMED BY EYE before it shipped
   — ratios were the floor, never the verdict). THE DEVICE VERDICT, all three
   arms: the ground PASSES — Dawn reads as a lighter brown room and Dusk reads
   as black, two genuinely different rooms, which is what the 4.81 → 12.94 ΔL*
   lift was for; the vellum PASSES — #E6D9C3 at 10.78:1 reads WARM, not muddy;
   the swatches PASS — each chip reads as the room it names. ⚠ THE AMENDMENT IS
   NOT A GENERAL LOWERING: the ≥12 bar stands for every theme, and ANY THEME
   SEEKING A VALUE UNDER 12 OWES ITS OWN DEVICE ROUND. Enforced at
   tests/ui/theme-ink.mjs, where BOTH arms moved in ONE edit (the ink bar and
   the cross-theme spread) because the trade between them IS the ruling —
   amending one alone would leave the other enforcing a property the ruling
   deliberately retired, to be silenced later by someone without the verdict in
   hand. At the lifted ground (#2E251D) holding
   the 12.98 RATIO would have pushed --tx to #FBEDD5, near-white. M held the HEX
   instead: --tx stays #E6D9C3 and Dawn now runs **10.78:1** against Day/Dusk's
   ~12.9. M's reasoning, recorded because the deviation must not read as drift:
   the parity ruling's own words — "a dark UI, not a DIMMED one" — were about
   keeping text STRONG across themes, and 10.78 is strong; near-white loses
   candlelit vellum, which is the theme's identity. "A lamplit room with
   paper-white text is a different room." ⇒ THE PARITY RULING YIELDS TO THE
   THEME'S IDENTITY AT THIS GROUND LEVEL. ⚠ PROVISIONAL: the device round can
   overturn it — if Dawn's text reads muddy on the phone, the fork reopens and
   #FBEDD5 (or an intermediate) is the other arm. Nothing here is settled until
   M has read in the room.

   The --dim ABSOLUTE LEVEL is RULED AND MET: Dawn 5.52 · Day 5.47 · Dusk 5.51
   on --g (measured 2026-08-06; M-ruled ~5.5:1, normalized in #139). Cross-theme
   spread 0.05 — PARITY is the property, and v2-meta-ink.mjs P4 is what guards
   it. There is no headroom: every value sits on its floor.
   (This line previously read "Dawn 4.47 · Day 3.04 · Dusk 3.28 … DEFERRED" —
   stale, and it would tell the next reader --dim has room it does not have.)
   ⚠ RE-DERIVED at the 2026-08-06 Dawn lift: Dawn's --dim moved #9C8C74 →
   #AC9A80 to HOLD 5.50 against the new ground. The ratio is the invariant; the
   hex is its consequence.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  color-scheme: dark;
  /* Dawn — THE LAMPLIT ROOM: walnut ground LIFTED 2026-08-06, candlelit vellum
     ink, the narrator's amber. The lift is M's ruling and its reason is a
     MEASUREMENT: at the old ground (#1B1511, L* 7.32) Dawn and Dusk (L* 2.51)
     sat 4.81 ΔL* apart — the two dark themes were closer to each other than
     EITHER was to its own hairline rule (ground↔rule 1.20 Dawn / 1.14 Dusk vs
     1.101 theme↔theme). Dawn now lands at L* 15.45, ΔL* 12.94 from Dusk, and
     spends 8 of the 86 points of Dawn↔Day headroom. Dawn is the LAMPLIT room;
     Dusk stays the dark one. ⚠ DERIVED, NOT PICKED, and the method is the
     reason parity survived: surfaces preserve each step's ΔL* above the ground;
     inks preserve their RULED CONTRAST RATIO against it, chromaticity kept by
     scaling along each token's own linear-light ramp. Holding ratios rather
     than hexes is what keeps cross-theme parity intact BY CONSTRUCTION —
     v2-shrine-contrast.mjs:420 records why that matters: theme-tokens-agree.mjs
     proves cross-FILE identity, NOT cross-THEME parity, so "re-tuning one theme
     alone would ship GREEN", and it nearly did once. */
  --g: #2E251D; --g-hi: #382C23; --tx: #E6D9C3; --dim: #AC9A80;
  --rule: #42362B; --amber: #E8A62F; --amber-hi: #F6BE55; --amber-glow: #f2b13d45;
  --play-ink: #241A0C; --dock-bg: #382F26; --dock-line: #4C4033; --btn-line: #5F4F3E;
  --ok: #7FA06A; --warn: #D9A83C; --bad: #FFD0C7;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, monospace;
  --sans: -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --serif: Charter, "Iowan Old Style", Georgia, serif;
  /* --err-line — the #err panel border, TOKENIZED 2026-07-31 (M's one-off row):
     a fixed literal PARTICIPATING in the state that most needs to read
     correctly in every theme — the corrected B2 test's one hit, within minutes
     of being written. Uniform across themes by inheritance from the literal
     era; a theme that needs its own value declares it here, one line. */
  --err-line: #7a3a2a;
  /* --here-w — the "you are here" row's weight (#chList .doorrow.on, both audio
     rooms). THE ONE THEME-CONDITIONAL WEIGHT (P3-2 device round, M-ruled
     2026-07-31): Day's amber on the SHEET ground (#ECE2CD, the surface the row
     actually paints on) measured WCAG 2.67, and the round judged weight-600
     (variant b) against a derived Day ink (#865E12, 4.51 — variant c) on the
     real cream chapter row. M's pick: b — "same amber, heavier, reads
     best on cream; no new token, no theme-conditional colour." The base value
     here equals the rooms' .doorrow base weight, so Dawn and Dusk paint the row
     UNCHANGED; Day's override below is the whole ruling. A weight that varies
     by theme is a cross-theme RELATIONSHIP (arm-2's own subject) and lives HERE,
     named, never as per-room host-rule literals that drift. Consumers:
     read.html + shrine.html `#chList .doorrow.on`. Pinned:
     tests/ui/v2-here-weight.mjs (the relation, both rooms, the token's homes). */
  --here-w: 400;
  /* ── THE CONTROL SCALE (r10 handoff, M's boards, 2026-09-06): ONE boxed-control
     geometry for every screen — height / radius / border width — resolved by every
     pill, tab, chip, field and boxed button, so a change lands in one place. The
     values are the shipped shelf tab's own (index.html .tab: 38 / 11 / 1px), promoted
     from that one control to the set. THEME-INDEPENDENT BY CONSTRUCTION: declared
     once, here, never in the per-theme blocks below and never in a document —
     theme-tokens-agree.mjs H4 reds a local declaration, H3 a re-declaration. Sizes,
     not colours; the SNUG-24 glyph ladder (16 · 22 · 24 · 32 · 44) is px in the
     documents, not a token. */
  --ctl-h: 38px; --ctl-r: 11px; --ctl-bw: 1px;
}
html.theme-cream {
  color-scheme: light;
  /* Day — Folio warmed */
  --g: #F3EBDB; --g-hi: #F8F1E4; --tx: #2B241A; --dim: #665D4D;
  --rule: #E0D6C0; --amber: #B5811C; --amber-hi: #D19E36; --amber-glow: #b5811c30;
  --play-ink: #FFF7E6; --dock-bg: #ECE2CD; --dock-line: #DDD1B6; --btn-line: #C9BB9E;
  --ok: #5E7A4A; --warn: #8F6E1B; --bad: #450B00;
  --here-w: 600; /* P3-2: the Day row reads by WEIGHT — ruling at :root's --here-w */
}
html.theme-night {
  /* Dusk — true black, full-contrast vellum (rehomed ruling #3). NO --ok /
     --warn / --bad here: Dusk INHERITS :root's (rehomed ruling #2 — the
     absence IS the ruling; do not "complete" this block). */
  --g: #0A0908; --g-hi: #12100C; --tx: #DCCFB6; --dim: #928572;
  --rule: #1C1915; --amber: #C68F28; --amber-hi: #D89B32; --amber-glow: #c68f2822;
  --play-ink: #1A1206; --dock-bg: #12100D; --dock-line: #241F18; --btn-line: #322A20;
}
