/* =================================================================
   SNITCH — design tokens.  Every literal in the build comes from here.
   Grounds step per chapter; the burnt orange is the only saturated
   thing on screen until the closing floods.
   Contrast figures in the comments are MEASURED (see §7.2), not
   estimated. Do not change a value without re-measuring.
   ================================================================= */

@font-face {
  font-family: 'Neue Pixel Grotesk';
  src: url("../fonts/NeuePixelGrotesk.ttf") format('truetype');
  font-weight: 400; font-style: normal;
  font-display: block;      /* display face — a swap flash at 96px is worse
                               than 200ms of veil, and the veil covers it */
}

/* Open Sans — SELF-HOSTED. No Google Fonts, no preconnect, no third-party
   request. A page about being watched does not phone a CDN. (§2.2) */
@font-face { font-family:'Open Sans'; src:url("../fonts/OpenSans-300.woff2") format('woff2');
             font-weight:300; font-style:normal; font-display:swap; }
@font-face { font-family:'Open Sans'; src:url("../fonts/OpenSans-400.woff2") format('woff2');
             font-weight:400; font-style:normal; font-display:swap; }
@font-face { font-family:'Open Sans'; src:url("../fonts/OpenSans-500.woff2") format('woff2');
             font-weight:500; font-style:normal; font-display:swap; }
@font-face { font-family:'Open Sans'; src:url("../fonts/OpenSans-600.woff2") format('woff2');
             font-weight:600; font-style:normal; font-display:swap; }
@font-face { font-family:'Open Sans'; src:url("../fonts/OpenSans-700.woff2") format('woff2');
             font-weight:700; font-style:normal; font-display:swap; }

/* The registry is the UNION of §2.1's five and MOTION's three (coordinator
   ruling, 2026-08-18). --flash (the hero birth-sweep) and --crumb (the
   breadcrumb cross-fade) were each registered in only one document; a
   missing registration silently breaks the interpolation it exists for. */
@property --head    { syntax: '<number>'; inherits: true;  initial-value: 0; }
@property --i       { syntax: '<number>'; inherits: false; initial-value: 0; }
@property --flash   { syntax: '<number>'; inherits: true;  initial-value: 0; }
@property --demote  { syntax: '<number>'; inherits: true;  initial-value: 0; }
@property --rule    { syntax: '<number>'; inherits: true;  initial-value: 0; }
@property --sw      { syntax: '<number>'; inherits: false; initial-value: 0; }
@property --crumb   { syntax: '<number>'; inherits: true;  initial-value: 0; }
/* --gx is GONE. It was declared in the old plan and used nowhere. */

:root {
  /* ---- GROUNDS (per-chapter temperature step, never a hue jump) ---- */
  --g-chrome:   #0B0B0A;   /* nav + footer: HARDER than the body, chrome sits ON the page */
  --g-hero:     #141413;
  --g-details:  #12100E;   /* warmer — under a desk lamp */
  --g-snitch:   #0E0F10;   /* colder — clinical */
  --g-log:      #141413;   /* neutral — evidence should look plain */
  --g-close:    #F06730;   /* FULL FLOOD — painted by #flood only. See §5.6. */
  --ground:     var(--g-hero);  /* live token, stepped per zone by chapter.js */

  /* ---- INK ---- */
  --ink-100: #F2F1EC;      /* primary + CAPTURED FACT only     16.8:1 on --g-details */
  --ink-200: #C3C3BC;      /* body copy                        10.7:1 on --g-details */
  --ink-300: #A9A9A1;      /* labels                            7.8:1 on --g-hero */
  --ink-400: #8A8A82;      /* mono telemetry                    5.3:1  ← text floor */
  --ink-500: #6A6A62;      /* NON-TEXT UI only (grey tick)      3.4:1  ← WCAG 1.4.11 */
  --ink-dim:#83817A;      /* UNLIT HERO WORD (--dim)           4.7:1  ← IT IS A SENTENCE */
  --close-ink: #141413;    /* on --g-close                      5.9:1 — solid, never alpha'd */

  /* ---- RULES ---- */
  --line:      #2A2A26;
  --line-soft: #1E1E1B;

  /* ---- BRAND: a two-state machine, not a gradient ---- */
  --brand: #DA5C31;        /* RESTING instrument colour        4.9:1 on --g-hero */
  --hot:   #F06730;        /* ACTIVE: read head, floods, capture, focus   5.9:1 */
  --live:  #A3DB27;        /* UNUSED. Its one sanctioned home — the live feed's dot — was
                              removed with the feed; nothing may claim it without a rule. */

  /* ---- GLITCH CHANNELS: never a standalone element colour ---- */
  --chan-r: #F06730;
  --chan-c: #2FD9D4;

  /* ---- TYPE ---- */
  --f-display: 'Neue Pixel Grotesk', 'Arial Black', sans-serif;
  --f-voice:   'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --f-machine: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;

  --t-h1:    clamp(42px, 7vw, 92px);
  --t-h2:    clamp(38px, 5.8vw, 78px);
  --t-h2-xl: clamp(34px, 8vw, 118px);   /* snitch only */
  --t-h2-cl: clamp(32px, 4.8vw, 62px);  /* closing only */
  --t-h3:    clamp(22px, 3vw, 30px);
  --t-lead:  24px;   /* the exhibit's lead reading */
  --t-body:  19px;
  --t-lede:  clamp(17px, 2.1vw, 21px);
  --t-note:  16px;
  --t-label: 15px;
  --t-mono:  13px;
  --t-micro: 11px;
  --t-nano:  10px;

  --lh-tight: 0.92;  --lh-body: 1.62;  --lh-lede: 1.55;  --lh-mono: 1.45;
  --lh-head:  1.05;  /* STEP 5.2 — the hero h1 only. The display face set in
                        CAPS at 80px needs air between the lines that 0.92
                        does not leave; _reference-hero.html sets 1.05 and
                        that is the number the human signed off. */

  --tr-display: 0.02em;   /* uppercase is ALWAYS positively tracked. Never 0, never negative. */
  --tr-label:   0.06em;
  --tr-micro:   0.10em;
  --tr-org:     0.16em;
  --tr-voice:  -0.025em;  /* sentence-case Open Sans only */

  /* ---- SPACING (8px base) ---- */
  --sp-1: 8px;   --sp-2: 16px;  --sp-3: 24px;  --sp-4: 32px;  --sp-5: 48px;
  --sp-6: 64px;  --sp-7: 80px;  --sp-8: 100px; --sp-9: 160px; --sp-exhale: 240px;
  --wrap-max: 1240px;
  --wrap-pad: 28px;
  --measure:      52ch;   /* p.roast → 480–620px line box at 1440. NOT 62ch, NOT 34em. */
  --measure-lede: 56ch;
  --measure-build:44ch;
  --nav-h: 81px;          /* re-published live by boot.js via ResizeObserver */

  /* ---- "YOUR DEVICE", THE EXHIBIT (§5.3, the human's 2026-08-18 brief) ----
     Twelve cards, three to a row, each with a big pixel glyph in a panel
     above its roast. The panels are SVG DATA URIS built by
     js/sections/device.js, and an SVG referenced as a background cannot
     see a custom property — so device.js reads every colour below off
     .exhibit-device with getComputedStyle and BAKES it into the string.
     The art still carries no colour literal of its own, exactly like
     every other surface in the build. Aliases, never new colours: the
     exhibit is the page's own palette, drawn six pixels at a time.

     THREE PANEL TREATMENTS, ONE RHYTHM (device.js PANEL). Three cards
     of nine take --px-fill with a dark glyph, two take --px-accent on
     --px-panel, and the remaining four rest at --px-ink on the same
     panel — the X of a three-by-three, and as much --hot as this
     section can carry without shouting. */
  --px-panel:   var(--line-soft);   /* the dark panel, one step off the ground */
  --px-fill:    var(--hot);         /* the accent panel — three cards of nine */
  --px-ink:     var(--ink-300);     /* 7.8:1 — the resting glyph */
  --px-accent:  var(--hot);         /* 5.9:1 — the accent glyph, on a dark panel */
  --px-onfill:  var(--g-details);   /* 6.1:1 on --px-fill — the glyph on an accent panel */

  /* the machine's own furniture, card one and nowhere else: its glass,
     its taskbar rule and its charging bolt. Two of them need a second
     value for the accent panel, where a dark screen would swallow the
     dark furniture standing on it. */
  --px-glass:   var(--g-details);   /* the screen, on a dark panel */
  --px-glass-f: var(--brand);       /* the screen, on an accent panel */
  --px-dim:     var(--ink-500);     /* 3.4:1 — WCAG 1.4.11 floor for non-text UI */
  --px-dim-f:   var(--close-ink);   /* the taskbar rule, on an accent panel */
  --px-lit:     var(--ink-100);     /* the charging bolt, and nothing else */

  --ex-gap:      var(--sp-3);       /* between cards, both axes */
  --ex-card-pad: 20px;              /* the panel bleeds out through it */
  --ex-panel-max: 320px;            /* a one-up card at 700px must not grow a
                                       525px panel; the slice takes the excess
                                       off the edges, never off the glyph */

  /* ---- "YOUR ENVIRONMENT", THE EXHIBIT (02) — THE WINDOW ----------
     The page looks out of the visitor's window: a pixel window frame
     with mullions, the weather behind the glass, the live local time on
     the sill, and a headline ticker crawling along the bottom edge. It
     is drawn by js/sections/environment.js in the SAME string sprite
     DSL exhibit 01 uses, published as ONE SVG data URI (--art-window)
     on .exhibit-environment, and — exactly as there — an SVG referenced
     as a background cannot see a custom property, so every colour below
     is read off that wrapper with getComputedStyle and BAKED into the
     string. The art carries no colour literal of its own.

     THE SKY IS CHOSEN BY THE VISITOR'S REAL LOCAL HOUR: dawn, day,
     dusk, night. Four three-stop ramps, top -> horizon. They are the
     ONLY new literals in this file, and they exist because a sky is the
     one surface on the page that is neither ink nor ground — it is what
     is BEHIND the glass. They stay inside the build's temperature story
     all the same: night is the cold --g-snitch family, dawn steps
     neutral, day and dusk step warm toward --g-details, and not one of
     them is saturated. The accent in this exhibit is four things and no
     more — the sun, the storm bolt, the lit windows of the far skyline
     after dark, and the ticker band. */
  --env-frame:    var(--line-soft);   /* the frame and the mullions — one object */
  --env-edge:     var(--line);        /* its 1px outer bevel, so it reads off the ground */
  --env-sun:      var(--hot);         /* 5.9:1 — the one accent in a daylit sky */
  --env-moon:     var(--ink-300);     /* the pale moon, and nothing else */
  --env-moon-lo:  var(--ink-500);     /* its craters — non-text UI, the 1.4.11 floor */
  --env-cloud:    var(--ink-300);
  --env-cloud-lo: var(--ink-500);     /* the cloud's underside */
  --env-precip:   var(--ink-200);     /* rain and snow */
  --env-bolt:     var(--hot);         /* the storm, and only when there is one */
  --env-lit:      var(--hot);         /* eight lit windows on the skyline, after dark */
  --env-temp:     var(--ink-100);     /* the temperature, in pixel digits */

  /* THE FOUR SKIES: hi -> mid -> horizon, top to bottom. The third stop
     is the HORIZON GLOW and it is deliberately the only warm value in
     the set — it is also the narrowest band in the drawing (8 art px of
     44) and the far skyline stands in front of most of it, so what the
     eye gets is a glow between buildings rather than a block of colour.
     The first browser pass had the ramp at 18/11/15 with the glow a
     third of the glass, and it read as a brown wall. */
  --sky-night-1: #07080A;  --sky-night-2: #0D1013;  --sky-night-3: #1B2026;
  --sky-dawn-1:  #101319;  --sky-dawn-2:  #212028;  --sky-dawn-3:  #45392F;
  --sky-day-1:   #1A1B1B;  --sky-day-2:   #2A2621;  --sky-day-3:   #4A3B2B;
  --sky-dusk-1:  #0F1013;  --sky-dusk-2:  #221D1E;  --sky-dusk-3:  #452F1F;
  --sky-star:    var(--ink-300);   /* eight of them, clear nights only */
  /* the far skyline, silhouetted on the horizon band. Two tones: one
     that survives the two dark skies, one that survives the two light. */
  --sky-city:     #0A0A09;
  --sky-city-day: #141311;

  /* ---- "YOUR BEHAVIOR", THE EXHIBIT (03) — THE TRACE --------------
     A pixel line chart of the visitor's own scroll, drawn live as they
     scroll it: time across, scroll depth down, flat where they stopped,
     a mark at every click. Unlike 01 and 02 it is a CANVAS, because it
     is the one drawing on the page that redraws — so these values are
     read off .exhibit-behavior with getComputedStyle once at boot and
     used as fillStyle, rather than baked into an SVG string. Aliases,
     never new colours: same instrument, drawn three pixels at a time.

     THE INVERSION IS THE WHOLE READING. Movement is drawn at --ink-300,
     the resting tone; a STATIONARY run is drawn at --ink-100, brighter
     and two art pixels thick, because a flat stretch is the most
     incriminating thing on this chart and should be the loudest thing
     on it. --hot is spent on exactly three things: the live edge, the
     click marks and the region a hovered roast is about. */
  --tr-panel:  var(--px-panel);   /* the plot well — exhibit 01's panel tone */
  --tr-line:   var(--ink-300);    /* 7.8:1 — the trace at rest */
  --tr-flat:   var(--ink-100);    /* a stationary run: brighter, and thicker */
  --tr-hot:    var(--hot);        /* the live edge, and the most recent segment */
  --tr-mark:   var(--hot);        /* a click */
  --tr-tick:   var(--ink-500);    /* 3.4:1 — WCAG 1.4.11 floor for non-text UI */
  --tr-band:   var(--brand);      /* the highlight behind a linked region */
  --tr-px:     3;                 /* ONE ART PIXEL, in CSS px. behavior.js
                                     multiplies it by devicePixelRatio, so the
                                     drawing is the same size on every screen
                                     and crisp on all of them. */
  --tr-h:      clamp(200px, 17vw, 260px);   /* the plot. A time series wants width. */

  /* ---- "WHO YOU ARE", THE EXHIBIT (04) — THE EMPTY BADGE ---------
     An employee ID card with a blank photo well, a '?' where the face
     goes, and every field ruled but unfilled. THIS IS THE SAME OBJECT
     AS THE FILLED BADGE AT THE AUTOFILL TRAP (§5.4a): sections.css
     declares the card's GEOMETRY once and unscoped, and only the SKIN
     is per-state — the dark values below for the empty badge in
     #details, the light-ground print treatment (§5.4a: #F2F1EC ground,
     #141413 ink, the hard 12px offset shadow) for the completed one in
     #snitch. The two must stay the same size, so every measurement in
     this block belongs to BOTH.

     The well is one SVG data URI (--art-photo) built by
     js/sections/identity.js in the same string sprite DSL as exhibits
     01–03 — and, exactly as there, an SVG referenced as a background
     cannot see a custom property, so the three colours it uses are read
     off .exhibit-identity with getComputedStyle and baked into the
     string. The art carries no colour literal of its own.

     ONE ACCENT AND NO MORE. The org line on the strip, the LIVE
     marker's square, and the block caret parked in the empty NAME
     slot — the field the page cannot fill. Everything else is ink. */
  --id-max:      640px;             /* the card, and the width the original
                                       stylesheet's own id-card block breaks at */
  --id-photo-w:  128px;             /* the photo well — the original's box */
  --id-photo-h:  150px;             /* 128:150 IS 6:7 IS identity.js's 30x35 grid */
  --id-measure:  46ch;              /* the copy under the badge. Narrower than
                                       --measure because it is CENTRED. */
  --id-card:     var(--line-soft);  /* the badge, one step off the ground */
  --id-strip:    var(--g-chrome);   /* its dark header strip — chrome sits ON the page */
  --id-foot:     var(--g-details);  /* the barcode band, a step back down */
  --id-well:     var(--g-details);  /* the empty photo well */
  --id-hatch:    var(--line);       /* TEXTURE, NOT A READING. The original's
                                       light hatch is #E6E4DC on #F2F1EC — a
                                       tone-on-tone step, not a pattern — and
                                       the dark one has to be the same kind of
                                       quiet or the well shouts louder than
                                       the '?' standing in it. */
  --id-glyph:    var(--ink-300);    /* the '?' — 7.8:1, the loud thing in the well */
  --id-rule:     var(--ink-500);    /* a ruled blank: PENDING, not broken */
  --id-rule-lit: var(--ink-400);    /* the NAME rule — the one fact that matters */
  --id-caret:    var(--brand);      /* the block caret, the same mark p.composing uses */
  --id-scan:     var(--hot);        /* THE READ HEAD sweeping the photo well while the
                                       badge is querying — doing read-head work, so it
                                       is read-head colour. Lives only inside
                                       [data-fetch="pending"], which snitch.js holds
                                       for 800ms and then replaces. */
  --id-dot:      var(--hot);        /* the LIVE marker. NOT --live: that token is
                                       reserved and currently has no element at all. */
  --id-bar:      var(--ink-500);    /* the barcode — furniture, not a reading */

  /* ---- THE FOOTER BRANDMARK — the wordmark as a field of loose pixels ----
     js/brandmark.js samples the hidden .ft-wordmark SVG into a 224x58
     grid and draws every filled cell as a small square on a canvas;
     they disperse like air near the cursor and settle back after it.
     Ported from oxigen.sa's ft-brandmark, whose fill is a hard
     '#ffffff'. It cannot be that here: --ink-100 is reserved for
     "primary + CAPTURED FACT only" (§INK, above), and spending it on
     furniture would make the lockup the loudest thing on the page.
     --ink-300 is the register the device exhibit already rests its
     glyphs at (--px-ink), so the wordmark reads as the same material
     as every other drawing in the build. Alias, never a new colour. */
  --ft-mark:  var(--ink-300);   /* 7.8:1 on --g-chrome — the lockup */
  --ft-mark-w: 1641 / 427;      /* the .ft-wordmark viewBox. .ft-word reserves the
                                   logo's box at this ratio and brandmark.js sizes
                                   its cells off that box, so the two MUST agree. */

  --env-col:    480px;   /* the window column at >=1140. The exhibit stacks below that. */
  --env-crawl:  38s;     /* the ticker's loop. environment.js republishes it from the
                            real headline length, so the crawl SPEED is the constant
                            and the duration is the thing that varies. */

  /* ---- THE POINTER (§2.3a): the system's own, no tokens (operator,
     2026-10-13). The pixel arrow and its --cur-* tokens are retired. */

  /* ---- RADIUS: zero everywhere. Two exceptions, both because they are lights. ---- */
  --r-0: 0;
  --r-dot: 50%;            /* status dot pulse ring ONLY */
  --r-slot: 999px;         /* id-card lanyard slot ONLY — a physical hole in a physical object */

  /* ---- EASING (CSS) ---- */
  --e-out:  cubic-bezier(0.16, 1, 0.30, 1);   /* arrival */
  --e-in:   cubic-bezier(0.70, 0, 0.84, 0);   /* dismissal */
  --e-wipe: cubic-bezier(0.70, 0, 0.20, 1);   /* the read head */
  --e-snap: cubic-bezier(0.85, 0, 0.15, 1);   /* capture */
  --e-soft: cubic-bezier(0.22, 1, 0.36, 1);   /* underlines, small UI */

  /* ---- DURATION ---- */
  --d-micro: 90ms;  --d-ui: 180ms;  --d-reveal: 500ms;
  --d-head:  550ms; --d-card: 620ms; --d-return: 1200ms;

  /* ---- GLITCH TOKENS ---- */
  --gl-amp-1: 3px;  --gl-amp-2: 5px;  --gl-amp-3: 8px;
  --gl-d-1: 180ms;  --gl-d-2: 340ms;  --gl-d-3: 500ms;
  --gl-scan-max: 0.30;   /* justified by the luminance measurement in §7.4 */
  --gl-grain:    0.03;

  /* ---- Z-INDEX (contract values preserved) ---- */
  --z-flood:   -1;       /* #flood — behind content, above the canvas */
  --z-nav:      100;
  --z-readhead: 9995;
  --z-vignette: 9996;
  --z-scan:     9997;
  --z-grain:    9998;
  --z-nag:      10001;   /* #sb-nag   — contract */
  --z-veil:     10002;   /* #boot-veil — must sit above the toasts */
}
