/* ==========================================================================
   TEMassist — DESIGN TOKENS
   --------------------------------------------------------------------------
   Source of truth:
     01_creative_direction.md  §6 type · §7 colour & light · §8 materials
                               §11 motion · §16 delivery notes
     02_website_blueprint.md   "The token system — inherited from the product"

   THE RULE UNDERNEATH (blueprint, superseded 2026-09-04 → option b):
     token VALUES are the product's, verbatim.  The SKIN is the site's.
     Where a creative draft and the product disagree on a value, the product
     wins — a colour that means "critical" must be the product's critical.

   THEME STATES.  The blueprint writes night as `body.night`.  This file uses
   `data-theme` on the root element instead, because there are THREE states,
   not two: an explicit day choice, an explicit night choice, and the default
   "system" state where nothing is stamped and only prefers-color-scheme
   separates them.  A `body.night` class cannot express the third.  Night is
   the showcase; the system preference is honoured and never overridden
   (blueprint §Accessibility).
   ========================================================================== */

:root {
  /* ---- PARCHMENT (day) — the "document" mode: the brief itself ---------- */
  --paper:      #f4efe5;
  --ink:        #182126;
  --panel:      rgba(255, 252, 246, .88);
  --line:       rgba(24, 33, 38, .12);
  --accent:     #0f5f73;   /* interactive — AND the non-severity data gap   */

  /* ---- The severity spine — SEMANTIC ONLY, never decoration (CD §7.1) --
     Always carried alongside a glyph and a word, never colour alone.       */
  --critical:   #8f1d14;
  --high:       #c5611f;
  --medium:     #b08a12;
  --low:        #356a48;

  /* ⛔ THE `-rgb` COMPANIONS, AND EVERY ONE OF THEM WAS A SILENT LOSS UNTIL 2026-09-11.
     The product's stylesheet writes its translucent fills as `rgba(var(--high-rgb), .13)`, and
     this file declared `--high` but never `--high-rgb`. A `var()` that names nothing is not an
     error — the whole declaration is invalid and the property falls back to its INITIAL value,
     which for `stroke` is `none`. That is how Act III's route map rendered as black blobs: every
     `v4-map-*` rule that named an `-rgb` token evaporated, and SVG's own defaults (`fill: black`,
     `stroke: none`) took over. The repo has met this exact trap before, as `--line-rgb`, which
     never existed at all. **Declare the companion beside the colour, always.** */
  --ink-rgb:      24, 33, 38;
  --accent-rgb:   15, 95, 115;
  --critical-rgb: 143, 29, 20;
  --high-rgb:     197, 97, 31;
  --medium-rgb:   176, 138, 18;
  --low-rgb:      53, 106, 72;

  /* The brief's two ground names, so its markup resolves here exactly as it does in the product.
     `--display` is the SHEET a card sits on and `--page-bg` the DESK behind it; at night the pair
     inverts and the display goes DARKER than its bezel, which is the night identity. */
  --display:  var(--paper);
  --page-bg:  #ebe4d6;

  /* ---- The one warm signal (CD §7.2) ----------------------------------
     CD lists the amber for night only.  On parchment the same role is
     carried by the hue it is derived from (--high), so the "one warm
     signal, nothing else saturated" grammar survives in both modes.       */
  --signal:     #c5611f;

  /* ---- Severity as TEXT (CD §7 vs §14, resolved) -----------------------
     The rank tokens above are the product's and are inherited VERBATIM — that rule is
     not negotiable. But two of them fail as small text on parchment: --high is 3.59:1
     and --medium 2.82:1, which misses AA and, for --medium, even the 3:1 UI floor. CD
     §14 requires AA on BOTH grounds and says the spine must stay legible on parchment
     AND obsidian, so the two rules genuinely collide in day mode.
     Resolved by scope rather than by overriding either: the true token keeps every
     FILL, BAR, STRIPE and MARK — where 3:1 is the correct floor and both pass — and
     these darkened-but-same-hue variants carry the rank WORD. Night needs no variant
     (7.61 and 10.49), so they simply resolve to the token there. Colour still means
     exactly what it means; it stops being what the text has to survive. */
  /* ⚠ SOLVED AGAINST THE TINT, NOT THE SHEET, AND THESE ARE THE PRODUCT'S VALUES. The first
     version of these was solved against bare parchment (#ab541b / #86690e) — and a rank word sits
     on a TINT of its own hue, up to 26%, so on a badge fill it still measured 3.98:1. Measured in
     the product, where the same four tokens now live; keeping two sets that disagree is the defect
     this file has been fixed for twice. */
  --critical-text: #8f1d14;   /* = the token; 4.88:1 on a 26% tint, 7.79 on the sheet */
  --high-text:     #8a4416;   /* 3.59 -> 4.64 on a 26% tint, 6.29 on the sheet */
  --medium-text:   #71580c;   /* 2.82 -> 4.62 on a 26% tint, 5.89 on the sheet */
  --low-text:      #2e5c3f;   /* 4.66 on a 26% tint, 6.72 on the sheet */

  /* ---- Derived values (not in the product's set; site-side only) ------- */
  --muted:      rgba(24, 33, 38, .64);
  /* ⚠ .38 measured 2.27:1 — and --faint is not decorative here. Its five use sites are
     the SAMPLE mark, "No threats at this phase", "Listed, never dropped", the attribute
     chips and the footer mark: every one a statement of honest absence or provenance. The
     build set the honesty layer in the least readable value on the page. Raised to AA on
     both grounds (.63 -> 4.57:1). A third tier of quietness is earned with SIZE and
     WEIGHT, never by dropping text under the contrast floor. */
  --faint:      rgba(24, 33, 38, .63);
  --hairline:   rgba(24, 33, 38, .12);
  --rim:        rgba(255, 255, 255, .55);   /* one light source: top/left  */
  --glow:       rgba(197, 97, 31, .16);
  --shadow:     0 22px 60px -28px rgba(24, 33, 38, .45),
                0 2px  10px -4px  rgba(24, 33, 38, .18);
  /* ⚠ `--starfield` IS GONE. It fed eight radial gradients that the real L0 plate replaced on
     2026-09-11, and a token declared but read by nothing is the mirror of the defect this
     stylesheet was just fixed for — `--mono` was USED and never declared; this would have been
     DECLARED and never used. Both are dead weight that reads as intent. */
  --band-a:     rgba(15, 95, 115, .12);
  --band-b:     rgba(53, 106, 72, .10);

  color-scheme: light;
}

/* ---- OBSIDIAN (night) — the "experience" mode, and the hero ------------
   Guarded so an explicit day choice still beats a dark OS setting.        */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="day"]) {
    --paper:    #0d1117;
    --ink:      #e6edf3;
    --panel:    #161b22;
    --line:     rgba(230, 237, 243, .16);
    --accent:   #58a6ff;

    --critical: #f85149;
    --high:     #e3923b;
    --medium:   #d6c23b;
    --low:      #3fb950;

    --signal:   #e0a458;   /* instrument amber — the only warm hue at night */

    /* The `-rgb` companions, night values — see the day block for why they exist. */
    --ink-rgb:      230, 237, 243;
    --accent-rgb:   88, 166, 255;
    --critical-rgb: 248, 81, 73;
    --high-rgb:     227, 146, 59;
    --medium-rgb:   214, 194, 59;
    --low-rgb:      63, 185, 80;

    --display:  var(--paper);
    --page-bg:  var(--panel);

    /* Night passes on all four (5.65 / 7.61 / 10.49 / 7.45), so the text
       variants resolve to the tokens themselves. */
    --critical-text: var(--critical);
    --high-text:     var(--high);
    --medium-text:   var(--medium);
    --low-text:      var(--low);

    --muted:    rgba(230, 237, 243, .60);
    --faint:    rgba(230, 237, 243, .49);   /* 2.60:1 -> 4.55:1 */
    --hairline: rgba(230, 237, 243, .16);
    --rim:      rgba(230, 237, 243, .22);
    --glow:     rgba(224, 164, 88, .18);
    --shadow:   0 30px 80px -30px rgba(0, 0, 0, .9),
                0 2px  12px -4px  rgba(0, 0, 0, .6);
    --band-a:   rgba(88, 166, 255, .14);
    --band-b:   rgba(63, 185, 80, .09);

    color-scheme: dark;
  }
}

/* ---- The explicit stamps, so a toggle wins in BOTH directions ----------
   ⛔ AND THE TWO NIGHT GROUNDS EMBEDDED IN THE PAGE TAKE THE SET FROM HERE, not from a copy of
   their own. `.act2__panel` (the product's shell, shown as a photograph of an instrument) and
   `.act3__map` are dark whatever the reader's theme is, so each used to carry its own transcript
   of these thirty declarations — and a transcript goes stale one line at a time. The panel's copy
   was missing exactly `--display`, which `:root` defines as `var(--paper)`: a var() token is
   substituted where it is DECLARED, so the panel inherited the computed PARCHMENT sheet and every
   card in it drew night ink on a cream ground — measured at 1.01:1, invisible, in day mode only.
   The map's copy was missing all thirty. An element selector in the token file is the price of
   having one definition; two definitions is what this cost. */
:root[data-theme="night"],
.act2__panel,
.act3__map {
  --paper:    #0d1117;
  --ink:      #e6edf3;
  --panel:    #161b22;
  --line:     rgba(230, 237, 243, .16);
  --accent:   #58a6ff;
  --critical: #f85149;
  --high:     #e3923b;
  --medium:   #d6c23b;
  --low:      #3fb950;
  --signal:   #e0a458;

  /* The `-rgb` companions, night values — see the day block for why they exist. */
  --ink-rgb:      230, 237, 243;
  --accent-rgb:   88, 166, 255;
  --critical-rgb: 248, 81, 73;
  --high-rgb:     227, 146, 59;
  --medium-rgb:   214, 194, 59;
  --low-rgb:      63, 185, 80;

  --display:  var(--paper);
  --page-bg:  var(--panel);

  /* Night passes on all four (5.65 / 7.61 / 10.49 / 7.45), so the text
     variants resolve to the tokens themselves. */
  --critical-text: var(--critical);
  --high-text:     var(--high);
  --medium-text:   var(--medium);
  --low-text:      var(--low);
  --muted:    rgba(230, 237, 243, .60);
  --faint:    rgba(230, 237, 243, .49);
  --hairline: rgba(230, 237, 243, .16);
  --rim:      rgba(230, 237, 243, .22);
  --glow:     rgba(224, 164, 88, .18);
  --shadow:   0 30px 80px -30px rgba(0, 0, 0, .9),
              0 2px  12px -4px  rgba(0, 0, 0, .6);
  --band-a:   rgba(88, 166, 255, .14);
  --band-b:   rgba(63, 185, 80, .09);
  color-scheme: dark;
}

/* ==========================================================================
   TYPE — the three-voice system (CD §6)
   Display carries the human/craft voice; Data carries every real flight
   datum; Body carries paragraphs and UI.  Which voice carries which content
   is the identity — see .u-data in base.css for the signature rule.

   NO THIRD-PARTY CDN (blueprint §Budget).  ~~The three named faces are to be
   SELF-HOSTED … Until they are, these stacks fall back to real system faces …
   Flagged, not faked.~~  **SUPERSEDED 2026-09-11 — they ARE self-hosted now**,
   five woff2 files in assets/fonts/ with their OFL licences beside them, 140 KB
   for the set.  The honesty of the old note is the reason it is struck rather
   than deleted: it said plainly that the identity was not being served, and it
   said so for four days.

   ⛔ AND THE PANEL VOICE CHANGED, WHICH IS THE POINT OF THE SWAP.  v1 named
   **Inter**.  CD v2 §6 names Inter as "the face every generated page reaches
   for" — an anti-pattern, not a neutral default — and rules **B612**, which was
   commissioned by Airbus for cockpit displays: legible small, under vibration,
   in low light, with unambiguous 1 I l and 0 O.  It is the single most
   on-subject typographic decision available to this product.  **B612 Mono**
   shares its skeleton, so a value set inside a sentence sits on the same
   metrics.  Plex is the recorded fallback if B612 is ever refused, and it stays
   in the stack for exactly that reason.  Fraunces is unchanged and is used LESS:
   one display line per page, never inside a panel.
   ========================================================================== */

/* Self-hosted, latin, `swap` — a face that has not arrived must never hold the
   page blank.  Same five files the brief serves, so the site and the product
   speak in one voice rather than two that resemble each other. */
@font-face { font-family: "B612"; font-style: normal; font-weight: 400;
             font-display: swap; src: url("../fonts/b612-400.woff2") format("woff2"); }
@font-face { font-family: "B612"; font-style: normal; font-weight: 700;
             font-display: swap; src: url("../fonts/b612-700.woff2") format("woff2"); }
@font-face { font-family: "B612 Mono"; font-style: normal; font-weight: 400;
             font-display: swap; src: url("../fonts/b612mono-400.woff2") format("woff2"); }
@font-face { font-family: "B612 Mono"; font-style: normal; font-weight: 700;
             font-display: swap; src: url("../fonts/b612mono-700.woff2") format("woff2"); }
@font-face { font-family: "Fraunces"; font-style: normal; font-weight: 100 900;
             font-display: swap;
             src: url("../fonts/fraunces-var.woff2") format("woff2-variations"); }

:root {
  --serif: Fraunces, "Spectral", "Libre Caslon Text", Georgia,
           "Times New Roman", serif;
  --mono:  "B612 Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo,
           Consolas, "Liberation Mono", monospace;
  --sans:  "B612", "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto,
           "Helvetica Neue", Arial, sans-serif;

  /* Modular scale, ratio 1.333, fluid (CD §6) */
  --fs-display: clamp(2.75rem, 8vw, 7.5rem);   /*  44 → 120px */
  --fs-act:     clamp(2rem, 4.4vw, 3.25rem);   /*  32 →  52px */
  --fs-lead:    clamp(1.5rem, 2.6vw, 2.125rem);
  --fs-sub:     clamp(1.125rem, 1.5vw, 1.375rem);
  --fs-body:    clamp(1rem, 1.05vw, 1.125rem); /*  16 →  18px */
  --fs-small:   .875rem;
  --fs-label:   .75rem;                        /* mono micro-label 12px     */
  --fs-data:    clamp(.875rem, 1.1vw, 1.125rem);

  --lh-display: .95;
  --ls-display: -.02em;
  --lh-body:    1.6;
  --ls-label:   .14em;                         /* instrument-panel register */

  /* ---- Space, on a baseline rhythm ------------------------------------ */
  --sp-1: .25rem;  --sp-2: .5rem;   --sp-3: .75rem;  --sp-4: 1rem;
  --sp-5: 1.5rem;  --sp-6: 2rem;    --sp-7: 3rem;    --sp-8: 4.5rem;
  --sp-9: 7rem;    --sp-10: 10rem;

  --measure: 34rem;                            /* ~65ch of body copy        */
  --shell:   78rem;
  --gutter:  clamp(1.25rem, 5vw, 4rem);

  /* ==== MOTION (CD §11, blueprint §Motion contract) ====================
     ONE easing curve for the whole system.  Everything eases out long.
     No bounce, no overshoot, nothing that reads as "fun" or "fast".      */
  --ease:    cubic-bezier(.16, 1, .3, 1);
  --t-hover: 150ms;    /* hovers — the blueprint's budget cap, and it is a CAP   */
  --t-micro: 300ms;    /* ticks, state changes that are not hovers               */
  --t-reveal: 600ms;   /* the blueprint's hard cap, used for every scroll
                          reveal so the budget table holds.                */
  --t-narr:  1500ms;   /* line-draw, decode                                */
  --t-drift-sky:  60s; /* L0 sky plate drift                               */
  --t-drift-band: 35s; /* L1 weather band                                  */
}
