/* ============================================================
   Studio design system: core tokens
   The single source of truth. Sheets link this file and use only
   var(--…) in their styles; tools/studio/check_tokens.py enforces it
   and recomputes every "pair:" comment below. Pair grammar:
   FG on BG ≥ N [text|non-text], or FG on BG field-only.

   Component rules (the plastic panel, screen and print) live in
   components.css, linked beside this file.

   Started 29 Oct 2026 (Task 5: CMF). Every colour is measured from
   a save on the Savee board "Studio Site" (ids beside each value,
   last 6 characters; full ids in cmf.html). Ratios are WCAG 2.x
   contrast against the stated background.

   Four materials: concrete (ground, a cast surface), clear plastic
   (panels), ink line (drawing), blue signal (state), and one element,
   the graphic devices (studio-system/core/mark.html; no tokens of
   their own). Two surfaces only (Aaron, 31 Oct: "just concrete-1"):
   paper (--paper), the flat light surface, every page's background,
   holding a light field the way deep blue holds a dark one; and
   concrete-1, the one cast surface inside each sheet's border. The rulebook is rules.md, beside this file. No red anywhere: the board's red is replaced by electric
   blue + deep blue (Aaron, 29 Oct).
   ============================================================ */

/* --- Fonts ------------------------------------------------------
   The pick (Task 8, Aaron, 29 Oct 2026): Founders Grotesk + Founders
   Grotesk Mono, Klim Type Foundry.
     Display  Founders Grotesk        (--font-sans)  Regular 400, Medium 500, Bold 700
     Body     Founders Grotesk Text   (--font-text)  Regular 400 + italic, Medium 500, Bold 700
     Labels   Founders Grotesk Mono   (--font-mono)  Regular 400, Medium 500
   Grotesk and Grotesk Text are one family in two optical sizes (Text
   is the small-size cut: looser spacing, sturdier joins), so this is
   still two voices, grotesque and mono (ban 11). The Condensed and
   X-Condensed cuts are not used.

   Licence: the bundle is USD 513, covering web (5,000 users/month)
   and desktop (5 users). Both confirmed on klim.co.nz on 29 Oct 2026.

   The files below are TEST FONTS; NOT LICENSED FOR RELEASE. They sit
   in fonts/founders/ (gitignored, evaluation only). Nothing built on
   them ships until the licence is bought and the release files
   replace these paths. Pages only ever use var(--font-*), never a
   family name.
   ---------------------------------------------------------------- */

:root {

  /* --- Type --------------------------------------------------- */
  --font-sans: "Founders Grotesk", system-ui, sans-serif;            /* display: headlines, the opener word, the mark */
  --font-text: "Founders Grotesk Text", system-ui, sans-serif;       /* body: paragraphs, leads, captions */
  --font-mono: "Founders Grotesk Mono", ui-monospace, monospace;     /* labels, numerals, specs; never body (ban 11) */
  --weight-regular: 400;
  --weight-medium:  500;
  --weight-bold:    700;

  /* Scale. Founders has a small cap height: 0.63 em (OS/2 sCapHeight
     630/1000 in all three cuts; x-height 0.437-0.446). A typical UI
     grotesque (Helvetica, Inter, the system face the old scale was
     drawn in) sits near 0.72 em. So at the same px size Founders
     reads about 12% smaller (0.63 / 0.72 = 0.875), and every step is
     set one notch larger to land the same cap height: old size x
     1.14, rounded. Body 16 -> 18 puts 11.3 px caps on screen, the
     cap height a 16 px system face gives. The ratios between steps
     are kept. Leading tightens a little (body 1.5 -> 1.45): the small
     x-height leaves more air between lines at the same leading. */
  --size-display: 272px;   /* was 240. Sheet opener word, about 1/4 of a 900 px sheet: 171 px caps */
  --size-h1:      64px;    /* was 56 */
  --size-h2:      30px;    /* was 26. Leads and panel heads */
  --size-body:    18px;    /* was 16. 11.3 px caps */
  --size-small:   15px;    /* was 13 */
  --size-label:   12.5px;  /* was 11. Mono, uppercase: 7.9 px caps */
  --leading-display: 0.86;
  --leading-heading: 1.0;
  --leading-body:    1.45;
  --tracking-display: -0.04em;  /* was -0.05; Founders Bold is close-fitted already */
  --tracking-heading: -0.01em;  /* was -0.03; Founders is close-fitted, and -0.02 crowded 30 px leads */
  --tracking-label:    0.06em;  /* was 0.08; the mono is wide, and the label is now larger */

  /* --- Space --------------------------------------------------- */
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-5:  24px;
  --space-6:  32px;
  --space-7:  48px;
  --space-8:  64px;
  --space-9:  96px;

  /* --- Sheet (guide sheets, Frame.io form) --------------------- */
  --sheet-w:   1440px;
  --sheet-h:   900px;
  --sheet-pad: 64px;
  --sheet-gap: 24px;       /* gap between stacked sheets on screen */

  /* ============================================================
     Material 1: CONCRETE (ground)
     Two surfaces, paper and concrete-1 (Aaron, 31 Oct: "concrete-4
     should not exist anymore. just concrete-1", then: paper and
     concrete-1 only). Measured from the concrete saves; neutral by
     choice: the slight warm/cool casts on the board (sky blue, pink
     band, rust) are left out. --ink is the near-black of the
     cantilever photo. Depth comes from paper fields, deep-blue fields
     and plastic panels, never from extra greys; hierarchy in text
     comes from size, weight and voice, never from grey text
     (rules.md 2). Concrete-2, -3 and -4 have left the system (below).
     ============================================================ */
  --paper:      #E5E5E5;  /* 7d9e19 stacked blocks, lit face.            flat, never textured: every page's background; it holds a light field (formerly concrete-0, renamed 31 Oct) */
  --concrete-0: var(--paper);  /* DEPRECATED alias of --paper (31 Oct), kept only so old studies and archives still render. check_tokens.py fails it on any other page */
  --concrete-1: #C2C2C2;  /* 7d9e19 stacked blocks, half-lit face.       1.41:1 on paper: decorative fill only. The one textured surface: the ground inside each sheet's border */
  --ink:        #1E2021;  /* 7ef970 cantilever block, underside.        12.99:1 on paper, 9.18:1 on concrete-1: AAA. All text and every line; the graphic devices' base (31 Oct) */

  /* pair: ink on paper ≥ 7 text */
  /* pair: ink on concrete-1 ≥ 7 text */
  /* Ink as a field (Aaron, 31 Oct; rules.md 3.1b): a flat --ink area may hold a section, drawing or data, with paper
     text and lines on it, as deep blue does. Paper on it 12.99:1; electric on it 3.64:1 (non-text). Graphic devices and
     panels may sit on it (6.9). */
  /* pair: paper on ink ≥ 7 text */

  /* --- DEPRECATED (31 Oct): concrete-2, -3 and -4 have left the system
     (Aaron: "just concrete-1"; paper and concrete-1 are the only
     surfaces). They stay defined, with their old values, only so the
     archives, the studies pages and cmf.html (rebuilt next, in CMF
     round 5) still render. check_tokens.py fails any use of them on a
     page that is not a studies page or an archive. In their place:
       concrete-2 lines   -> ink, at the right weight (rules.md 3.2)
       concrete-3 text    -> ink; hierarchy from size, weight and voice
       concrete-4 ground  -> deep, a dark field (rules.md 5.1; ink is the other, 3.1b)
       concrete-4 device base -> ink (rules.md 6.2)
     Their contrast pairs are removed. */
  --concrete-2: #858281;  /* DEPRECATED. was 7f9181 board-formed stair wall: rules, construction */
  --concrete-3: #555959;  /* DEPRECATED. was 7d033e vault columns, shadow side: muted text */
  --concrete-4: #282828;  /* DEPRECATED. was 7d9e19 stacked blocks, deep shadow: the dark ground, the devices' base */

  /* --- Concrete as a cast surface (Aaron, 30 Oct; one texture, 31 Oct)
     "having the concrete block shapes in this system is too literal.
     putting concrete texture on all the concrete colors will help
     translate the structural feeling". Structure is carried by the
     material, not by block shapes. Then (Aaron, 31 Oct): "there should
     only be one concrete texture. just concrete 1. delete the others.
     rename the flat concrete 0 to something more appropriate. It will
     serve the same purpose as deep blue." So:
       paper       flat, never textured (formerly concrete-0): the page
                   background outside a sheet's fine inked border, and
                   light fields inside it, the way deep blue holds a
                   dark one.
       concrete-1  the ONLY textured surface: the ground inside the
                   border.
     Then (Aaron, 31 Oct): "concrete-4 should not exist anymore. just
     concrete-1": paper and concrete-1 are the only two surfaces, and
     concrete-2 to -4 left the system (deprecated above). Depth on the
     ground comes from paper fields, deep-blue fields and plastic
     panels. Never a surface on the same surface (no concrete-1 on
     concrete-1, no paper on paper). The concrete-0 tiles are archived in
     studio-system/archive/2026-10-31-concrete-c0-tiles/; the concrete-2
     to -4 tiles were deleted on Aaron's word (31 Oct; the generator
     rebuilds them deterministically from its study field). The masses
     (block, slab, step, reveal, slit, slot, plinth, cantilever) and
     their tokens are retired, archived in studio-system/archive/
     2026-10-30-tokens-pre-redo.css and -components-pre-redo.css.

     The texture is E3 W4 S2, locked 30 Oct (texture round 6, Aaron
     picked water step S2): stacked tie-hole formwork panels 480 x 240,
     a fuller cast surface, seams that weep, subtle water runs (gain
     0.73, two thirds of S1). The tile is built by
     tools/studio/build/build_concrete_texture.py into
     concrete-texture/: concrete-c1@1x.png (1440 x 960 px, 1x relief
     0.99) serves by default and concrete-c1.png (2880 x 1920 px) on
     high-DPR screens, through image-set(). It is seamless, shown at
     1440 x 960 CSS px, and its mean is its token (dE76 0.00).
     components.css applies it: .c-concrete-1; .c-paper is flat.

     Contrast (rulings, 30 Oct; rules.md 2.4):
       Flat surfaces (paper) are judged exactly.
       On the textured concrete-1, text pairs are judged at the 1st/99th
       percentile of the tile, not every pixel: tie-hole bottoms and
       seams always dip, they are under 1% of the area and never form a
       glyph's ground (ink on concrete-1: 7.21 there at 1x, 7.12 at 2x,
       stats.json). Non-text pairs on it are judged on the tile mean,
       which is the token.
       Electric on concrete-1 is 2.13:1, under the 3:1 non-text floor:
       a signal never sits on concrete-1; it sits on paper or a panel
       over paper. */
  --concrete-tile-w: 1440px;   /* the tile as shown, in CSS px; it repeats down every 960 px (four panel rows), never across a 1440 sheet */
  --concrete-tile-h: 960px;

  /* ============================================================
     Material 2: CLEAR PLASTIC (panels)
     The frosted panel. From 349969 (clear iPod: board visible
     through, sharp rim highlight), 3740a7 (clear Walkman: frosted
     base, internals soft, white rim), 34aa72 (MiniDisc: water-clear
     shell, faint cool cast). Frosting always carries its edges.

     Locked 30 Oct (Aaron): F2 soft frost with the original Edge-lit
     edge. "too generic. if the ink and the concrete texture are raw.
     this should be sleek to contrast. it should still read as frosted
     glass", "it should read like an apple product", then "I like soft
     frost without the electric edge lit. I don't want it to compete
     with the [graphic devices]. let's resort to the original edge lit". So:
     a soft frost with a cool neutral cast, a white rim inked top and
     left, a faint deep-blue far edge inked right and bottom, squircle
     corners, a soft, tight shadow. No electric, no glow. Built and
     measured in plastic-studies.html (build_plastic_studies.py, round
     3). The 14 px heavy-blur panel it replaces is archived in
     studio-system/archive/2026-10-30-tokens-pre-redo.css.
     ============================================================ */
  --plastic-fill:   rgba(240, 245, 246, 0.52);         /* the frosted near-white of 3740a7 cooled toward 34aa72; the frosts take a share of it */
  --plastic-frost:      color-mix(in srgb, color-mix(in srgb, var(--plastic-fill) 69%, transparent) 94%, var(--deep));  /* F2 soft frost on a light ground: fill 0.36, with a 6% deep-blue cast */
  --plastic-frost-on-deep: color-mix(in srgb, color-mix(in srgb, var(--plastic-fill) 29%, transparent) 94%, var(--deep));  /* over a deep-blue field (.on-deep): fill 0.15, so paper text holds; the white rim carries the edge there (rules.md 4.3) */
  --plastic-frost-on-4: var(--plastic-frost-on-deep);  /* DEPRECATED (31 Oct, concrete-4 left the system): the old smoked frost over concrete-4, the same mix. Kept so archives and cmf.html render; check_tokens.py fails it elsewhere */
  --plastic-blur:   6px;                                 /* soft: what lies under reads as shape */
  --plastic-backdrop: blur(var(--plastic-blur)) saturate(80%);   /* backdrop-filter value; also the print show-through copy's filter */
  --plastic-grain-size: 160px;                           /* the faint, fine frost grain tile (plastic-studies/grain-f2.png, 320 px at 2x) */
  --plastic-edge:   rgba(255, 255, 255, 0.9);            /* the rim: top and left, inked fine (1 px), 0.9 px inside the edge */
  --plastic-edge-far: rgba(155, 33, 0, 0.6);             /* the far edge: right and bottom, inked bold (2 px). --deep (#9B2100) at 60%, written out so ink_rules.py can bake it. Decorative: 3.04:1 on paper and lost on a deep field; the white rim carries the edge */
  --plastic-shadow: 0 1px 1.5px color-mix(in srgb, var(--ink) 14%, transparent), 0 10px 20px -8px color-mix(in srgb, var(--ink) 30%, transparent);   /* soft and tight: a contact line and a short falloff */
  --raised-shadow: 0 24px 32px -12px color-mix(in srgb, var(--ink) 24%, transparent);   /* a field in the air during a motion (Aaron, 4 Nov 2026: the Say hello letter flies raised): a longer, softer fall. It always resolves to --plastic-shadow as the field sets (rules.md 4.1, ban 15) */
  --plastic-shadow-print: drop-shadow(0 1px 1.5px color-mix(in srgb, var(--ink) 14%, transparent)) drop-shadow(0 5px 7px color-mix(in srgb, var(--ink) 16%, transparent));   /* filter value for paper */
  --plastic-radius: 22px;                                /* with corner-shape: squircle (Chromium 139 and later; Safari and Firefox fall back to this radius, round) */

  /* The live button (Aaron, 30 Oct: "the live buttons should be electric edge lit"): the one main action in a
     view, from plastic round 2's lit Edge-lit. An electric edge inked all round, baked at this one size by
     tools/studio/build/build_live_button.py (live-button/edge.svg); components.css .c-button-live. No glow (Aaron,
     31 Oct, CMF round 7: "the glow effect on the live button can come off"; ban 7 has no exception): the G2 glow and
     its --button-live-glow reach are archived in studio-system/archive/2026-10-31-tokens-pre-r7.css. */
  --button-live-w: 232px;
  --button-live-h: 56px;
  /* The fill (Aaron, 31 Oct: "the live buttons should also have an electric blue fill"): --electric, solid on
     screen and on paper; the inked edge stays. Its label is bold at --size-h2 (30 px, large text: the
     3:1 floor, ban 4's one exception). Paper on electric is 3.01:1, ink on electric 4.32:1. The label is paper (Aaron,
     31 Oct, CMF round 6). Square corners, no shadow: the rounded corner and the contact shadow stay plastic-only. */
  --button-live-label: var(--paper);

  /* pair: paper on electric ≥ 3 */

  /* Print fallback (default). Chromium's print-to-PDF drops
     backdrop-filter, so on paper a panel takes an opaque, pre-mixed
     fill: the frost composited over the ground it sits on (per
     channel, premultiplied as color-mix does). components.css applies
     it; a panel over a deep-blue field adds the class .on-deep. */
  --plastic-print-fill-on-0: #E4DFDD;  /* --plastic-frost over paper (the "0" is the old concrete-0 name). ink on it 12.38:1 */
  --plastic-print-fill-on-deep: #A73F23;  /* --plastic-frost-on-deep over deep. paper on it 4.93:1 */
  --plastic-print-fill-on-ink: #433E3D;   /* --plastic-frost-on-deep over ink, for .on-ink (CMF round 7: a panel on an ink field, rules.md 3.1b). paper on it 8.36:1 */
  --plastic-print-fill-on-4: #44474B;  /* DEPRECATED (31 Oct): the old smoked frost over concrete-4. Kept so archives and cmf.html print; check_tokens.py fails it elsewhere */

  /* pair: ink on plastic-print-fill-on-0 ≥ 7 text */
  /* pair: paper on plastic-print-fill-on-deep ≥ 4.5 text */
  /* pair: paper on plastic-print-fill-on-ink ≥ 4.5 text */

  /* ============================================================
     Material 3: INK LINE (drawing)
     Stroke widths, ISO 128 style 1 : 2 : 4.
     ============================================================ */
  /* Every line in the system is drawn in the ink hand, the frames
     included (Aaron, 30 Oct; "Ink hand" below, banned.md 30): drawings,
     rules, row and table rules, underlines, the focus ring, sheet and
     tile frames, the plastic rim. Every weight is sourced from a
     drawing on the board. */
  --line-hair: 0.5px;  /* dimension, extension, construction, contour. 4e8cfc patent figure hairlines; 53f358 blueprint dimension lines; 63825e contour lines */
  --line-fine: 1px;    /* part lines, leaders, hidden edges. 4d9e66 exploded view part lines; cc0eb6 refill bottle hairline */
  --line-bold: 2px;    /* the edge: where the object ends. 52184c shoe outline; 4b9ca4 instrument silhouette */
  --line-dash: 6 4;    /* hidden edge (on --line-fine) and construction / centre line (on --line-hair). 52184c vertical dashed guides between views; stroke-dasharray, in user units */
  --line-ink:  var(--ink);          /* line on a light ground */
  --line-ink-dark: var(--paper);    /* line on a deep field */

  /* --- Raw ink (direction study, cmf.html sheet 06; nothing chosen yet)
     Two ways raw ink could enter the line system. B, inked technical:
     the drawing keeps its geometry, drawn by an ink hand (built as path
     geometry on the sheet, no token needed). C, print layer: the ink
     line overprinted by a flat deep second colour, out of
     register, with an edition number (6ad56e09 two-colour frog print,
     "119"). The filter ids live on the sheet that uses them:
     #ink-press (ink-edge texture), #ink-plate (uneven blue plate),
     #ink-grain (paper grain), and the screen clips #ink-screen-lit /
     -shade (C2's dot screen is geometry: Quartz mis-scales SVG pattern
     tiles in Chromium PDFs). */
  --ink-misregister-x: 5px;    /* second colour plate off register, right */
  --ink-misregister-y: -3px;   /* and up. A few px: read as print, not as an error */
  --ink-overprint: multiply;   /* mix-blend-mode of the second colour: it overprints the ink, so the ink stays dark */
  --ink-grain: 0.22;           /* opacity of the paper-grain speckle layer (C3) */
  /* --- Ink hand (Aaron, 30 Oct: B1×B3 #3 + #6 joins) ----------------
     The system's hand: how a pen draws EVERY line in the system, not
     only drawings (Aaron, 30 Oct: "Every single line. It's part of the
     design system."). Chosen from the studies in ink-studies.html:
     along a run it is number 3 ("Wander-led", sheet 02); at joins and
     line ends it takes number 6's behaviour ("At joins": a bigger
     swell and the irregularity gathered where the nib turns or lifts).
     The line keeps its geometry and its 1 : 2 : 4 weights. Each line
     becomes a filled outline. The join behaviour fades into the run
     over the same gaussian as the swell, so there is no step.
     tools/studio/ink_hand.py reads these values from this file and
     nowhere else (IN.svg -> OUT.svg); tools/studio/ink_rules.py uses
     it to draw the CSS rule assets (ink-rules.css). Lengths are px at
     1 : 1. */

  /* Along runs: number 3 */
  --ink-hand-swell: 1.25;          /* curves (no joins): the broad-nib peak, times the line's own weight */
  --ink-hand-thin: 0.9;            /* width in the middle of a long run, times the weight */
  --ink-hand-run-min: 10px;        /* a run shorter than this does not thin */
  --ink-hand-run-full: 60px;       /* a run this long thins all the way to --ink-hand-thin */
  --ink-hand-wander: 0.7px;        /* the most an edge moves on the 2 px outline; a line of weight w moves sqrt(w / 2) of it */
  --ink-hand-period: 5.5px;        /* wander period of one edge (the distance it takes to turn) */
  --ink-hand-period-b: 4.7px;      /* the other edge, so the two never move together */
  --ink-hand-rough-period: 46px;   /* period of the envelope that decides where along a run it roughens */
  --ink-hand-rough-floor: 0.15;    /* the calmest stretch keeps this share of the wander */
  --ink-hand-drift: 0.13125px;     /* slow sideways drift of the whole line */
  --ink-hand-drift-period: 38px;
  --ink-hand-nib: 35deg;           /* curves: width follows a broad nib held at this angle */

  /* At joins and line ends: number 6 */
  --ink-hand-join-swell: 1.6;      /* width at a join or line end, times the weight; the round pool there is this wide */
  --ink-hand-join-wander: 0.8px;   /* edge irregularity at a join or end, on the 2 px outline (sqrt(w / 2) of it on weight w) */
  --ink-hand-join-reach: 5.5px;    /* gaussian reach of the swell from the join */
  --ink-hand-join-shape: 0.6;      /* the join wander follows the swell's gaussian to this power: a little wider, so it fades smoothly into the run */

  --ink-hand-seed: 11;             /* noise seed; each line offsets it by its order in the drawing */
  --ink-hand-roles: bold fine hair dash;  /* the roles that take the hand: all of them. Outline, part, hairline (leaders, dimensions, rules, frames) and dashed construction */

  /* --- Ink rules: the hand on HTML lines (components.css, .ink-*) ---
     CSS borders, outlines and underlines cannot carry the hand, so
     every rule and frame is a mask over a pre-inked line asset
     (generated into ink-rules.css by tools/studio/ink_rules.py), cut
     to the element's length, never stretched or tiled. A rule is two
     halves: the start of one inked line (its pooled start at the
     element's start) and the end of another (its pooled end at the
     element's end), meeting in the middle with a small overlap. A
     frame is four such edges; their pooled ends meet at the corners,
     which are joins. */
  --ink-rule-box: 8px;             /* the asset's thickness: room for the widest pool and the wander */
  --ink-rule-pad: 4px;             /* the line's centre sits this far inside the asset box */
  --ink-rule-overlap: 2px;         /* each half runs this far past the middle, so the halves join without a gap */
  --ink-rule-max-h: 2600px;        /* the longest line across: a rule, edge, pill or dash, or a frame's or rim's drawing box. Each half asset is max / 2 + overlap = 1302 long. A frame's drawing box is the element plus 2 x (--ink-rule-pad + --ink-offset), so a frame at offset 0 may be 2592 wide. Covers a 2560 desktop (the Say hello page's ground frame is 1880 wide at 1920 and 2520 at 2560) and A4 (794 x 1123) either way up. Was 1596 until 5 Nov 2026 */
  --ink-rule-max-v: 2400px;        /* the longest line down (half asset 1202). A frame at offset 0 may be 2392 high. The Say hello page's ground frame is 1425 high on a 320 phone showing NOT SENT, and as high as the window less 16 px on a tablet or portrait screen (1904 at 1920 tall). Was 1276 until 5 Nov 2026 */

  /* rule: ink on deep is 2.03:1, under the 3:1 non-text floor.
     Where the blue plate overprints a line, the line fades: a
     decorative overprint only. The drawing must read from the parts
     on paper; no text sits on the blue plate (text on deep is
     paper or nothing). */

  /* ============================================================
     Material 4: THE SIGNAL (state)
     LOCKED 2 Nov 2026 (Aaron, after hue studies in
     studio-system/core/variants/): one orange family. Electric
     #D46200 and deep #9B2100, both OKLCH hue about 34 to 50, picked
     by eye from studies built by tools/studio/build/build_cmf_hue.py.
     The electric is the passing colour nearest to orange B (#FF972F):
     3.01:1 on paper, as far toward B as the 3:1 floor allows. The
     deep is the lightest at its hue that keeps every pair below
     (concrete-1 on it 4.51, paper on the frost over it 4.93), leaned
     toward red so it reads deep orange, not brown.
     Two layers (Aaron, 2 Nov: "new token system for the different
     colors. do not keep the names the same and just change the
     colors"):
       colour tokens, named for the colour: --orange-electric,
         --orange-deep; and the first palette, kept for the blue copy
         (studio-system/core/variants/blue/) and the studies pages:
         --blue-electric, --blue-deep (deprecated outside studies
         pages and archives; check_tokens.py fails them elsewhere);
       role tokens, which every page, script and pair uses: --electric
         (the signal) and --deep (the dark field). A palette change
         repoints these two lines.
     The blue history follows.
     The board's blue saves sat between hue 200 and 234 (sampled
     saturated pixels: 3046b7 #0D66A4, 34aa72 #1B4C8D, 53f358
     #204989, 548019 #264184, 52872c #465BA3). None of them is
     electric; the hue comes from the board, the chroma is Aaron's.
     No glow, no gradient (buyer reading: blue glow + HUD reads as
     an AI startup).
     ============================================================ */

  /* Electric: interaction, state, and the one highlighted thing per
     view (focus ring, active row, current link, progress line,
     marker square). NON-TEXT ONLY: it carries no text and no text
     sits on it. Hue 223, inside the board's range (34aa72 label
     tab, 53f358 blueprint ink), chroma pushed to full. */
  --orange-electric: #D46200;  /* the signal since 2 Nov. 3.01:1 on paper (non-text ≥ 3); 4.32:1 on ink, the graphic devices' screen and outline over their ink base. White on it is 3.79:1, which FAILS AA (4.5); paper on it 3.01:1, ink 4.32:1: not a text ground, except the live button's large bold label (ban 4's one exception) */
  --blue-electric: #2F6BFF;    /* the first palette's signal (to 2 Nov): the blue copy and the studies pages only */
  --electric: var(--orange-electric);   /* ROLE: the signal. Use this, never a colour token */

  /* pair: electric on paper ≥ 3 non-text */
  /* pair: electric on ink ≥ 3 non-text */

  /* Deep: holds a field (rules.md 5.1): a section, a drawing or data,
     like a blueprint ground, sized as a field, never as a swatch. The
     dark counterpart of paper. Ground blocks and blueprint fields. Text-capable on the
     light ground. Not in board-tags.json's measured colours (those
     five are pixel-share clusters, so the sofa's ink blends with the
     paper into #4E6D9A). Sampled from 6ad56c9c3c9caf435653f358 (blue
     blueprint sofa, 420x280 PNG): thumbnailed to 300x200; kept pixels
     with HSV hue 198-259 deg, saturation >= 0.45, value >= 0.25
     (9,843 px, 16.4%, bounding box x47-270 y56-138: the drawn sofa,
     the paper excluded); ranked by saturation x value; #204989 is the
     90th percentile (median of the selection #274F8C). */
  --orange-deep: #9B2100;    /* the dark field since 2 Nov. 6.38:1 on paper. One of the system's two dark fields, with ink (rules.md 5.1, 3.1b) */
  --blue-deep: #204989;      /* the first palette's dark field (to 2 Nov): the blue copy and the studies pages only */
  --deep: var(--orange-deep);   /* ROLE: the dark field. Use this, never a colour token */

  /* pair: deep on paper ≥ 4.5 text */
  /* pair: paper on deep ≥ 4.5 text */
  /* pair: concrete-1 on deep ≥ 4.5 text */
  /* rule: electric on deep is 2.12:1. Electric never sits
     on a deep field; state on deep uses paper. */

  /* --- Focus ----------------------------------------------------
     The focus ring is .ink-focus in components.css: an inked bold
     frame in electric, --focus-offset outside the part, shown on
     :focus-visible. No outline anywhere (banned.md 30). */
  --focus-ring: var(--line-bold) solid var(--electric);   /* RETIRED 30 Oct: a border shorthand, banned. Kept only so layers.html and motion.html keep their ring until they are converted to .ink-focus; nothing new may use it */
  --focus-offset: 3px;

  /* --- Layers (Task 6: layers.html) -----------------------------
     Stacking order of the four materials, bottom to top. Every UI
     part sits on exactly one layer (mapping in layers.html). Gaps of
     10 leave room for a part's own inner order. */
  --layer-concrete: 0;    /* page ground: the cast surface */
  --layer-plastic:  10;   /* panels, anything that frosts what is beneath */
  --layer-line:     20;   /* drawings, labels, callouts, grid */
  --layer-signal:   30;   /* blue states and the active element */

  /* --- Motion (Task 9: motion.html) -----------------------------
     Durations and easings for the five primitives. Pages never write
     a duration or a curve; they use these. Each primitive is
     one-shot (no loops) and has a reduced-motion fallback: the end
     state, or a crossfade of --dur-fast (150 ms, the ceiling). */
  --dur-fast:       150ms;   /* reduced-motion crossfade; never longer */
  --dur-resolve:    1800ms;  /* points become line (6ad56c0e) */
  --dur-stagger:    10ms;    /* resolve: gap between one point and the next */
  --dur-track:      2600ms;  /* callouts follow a subject (6ad56c83) */
  --dur-boot:       4800ms;  /* init readout, first paint to ready (Heron boot, about 6 s) */
  --dur-step-snap:  1ms;     /* boot: a row appears whole; the shortest legal animation, so the step easing has something to hold */
  --dur-boot-step:  600ms;   /* boot: gap between table rows */
  --dur-separate:   1000ms;  /* layers pull apart (exploded views) */
  --dur-separate-step: 160ms; /* separate: gap between one layer and the next */
  --dur-sweep:      2200ms;  /* a band of light crosses a surface (6ad57058) */
  --dur-sweep-step: 180ms;   /* sweep: gap between one tread and the next */

  --ease-resolve:  cubic-bezier(0.22, 0.61, 0.36, 1);  /* decelerates into place */
  --ease-track:    cubic-bezier(0.45, 0, 0.25, 1);     /* the subject eases; its callouts trail it */
  --ease-boot-line: linear;                            /* the progress line */
  --ease-boot-count: steps(20, end);                   /* the percentage moves in 5 % steps, never a blur of digits */
  --ease-boot-rows: steps(1, start);                   /* a row appears whole, no fade, no flash */
  --ease-separate: cubic-bezier(0.65, 0, 0.2, 1);      /* in and out, mechanical */
  --ease-sweep:    cubic-bezier(0.4, 0, 0.2, 1);       /* light crosses and settles */
}
