/* ==========================================================================
   Design tokens — colour, type, layout, motion, layers.
   One source of truth. Nothing below this file invents a curve or a z-index.
   ========================================================================== */
:root {
  /* --- Colour -----------------------------------------------------------
     The page ground is matched to the loading video's, so the hand-off from
     the loading card lands on the same room rather than on a different one.

     Sampled off the last frame of the old loading video, away from the logo:

         top      luma 16.6   rgb ~14.5 / 15.5 / 18.5
         middle   luma 12.3   rgb ~12.3 / 11.9 / 14.7
         bottom   luma  9.6   rgb  ~8.9 /  9.6 / 11.4

     Two things came out of that measurement. The falloff runs bright-to-dark
     DOWNWARD — the previous gradient ran the other way and was 2.4x too
     bright at the bottom — and the whole field carries a cool cast, blue
     sitting about 4 above red.

     The values below are NOT those numbers. They were derived by subtracting
     the contribution of the old full-viewport texture layer, since removed: it
     composited an opaque mid-grey at ~0.0257 opacity, which lifted everything
     beneath it by 0.0257 * (128 - base), close to +3. Painting the measured
     values here would, under that layer, have put the finished page about three
     levels brighter than the video everywhere — a small, flat mismatch the
     layer itself caused. With the layer gone these values now sit about three
     levels below the measured video; retuning the ground back up is a separate
     job from the texture removal.

     The original derivation was base = (target - 0.0257 * 128) / 0.9743, then
     nudged to keep the cast. Composited under the old layer these landed at
     16.4 / 12.4 / 9.5 against the video's 16.6 / 12.3 / 9.6.
     ---------------------------------------------------------------------- */
  --color-bg-top:    #0D0D11;
  --color-bg-mid:    #09090C;
  --color-bg-bottom: #060609;
  --color-text:      #F1EDE4;
  --color-dim:      rgba(241, 237, 228, 0.62);
  --color-rule:     rgba(241, 237, 228, 0.14);

  /* --- Type ------------------------------------------------------------- */
  --font-mono:  'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --font-serif: 'Bespoke Serif', Georgia, 'Times New Roman', serif;
  --font-chivo: 'Chivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --fs-body:    12px;
  --fw-mono:        400;
  --fw-mono-strong: 500;
  --fw-serif:       500;
  --lh-heading: 1;
  --lh-body:    1.5;
  --lh-stacked: 1.35;
  --ls-mono-ui: 0.08em;
  --ls-serif:  -0.02em;

  /* --- Layout ----------------------------------------------------------- */
  --page-margin: 70px;

  /* Proportional scale units, in design px of the 1440 frame.
     --u  : page chrome. Desktop is set to look the way it does in a 1920px
            window at every width (author, 2026-09-25): 1px at 1920 and up,
            shrinking with the window below it (100vw / 1920). Tablets and
            phones re-base it (css/responsive.css).
     --us : the TV/tape scene (breakpoint-overridden) */
  --u:  min(1px, calc(100vw / 1920));
  --us: var(--u);

  /* Scene geometry, in design px (unitless so they compose in calc) */
  --tape-w: 446;
  --tape-h: 61;
  --tape-pitch: 61;
  --tape-shift: 46;

  /* Tape at rest is an unlit object; hover is the signal reaching it.
     Tuning knob — reviewed in the manual-testing phase. */
  --tape-dim: 0.55;

  /* The television's BOX top sits 196 design px above the viewport bottom,
     but TV2 carries 10 transparent rows at the top of its 912x900 frame, so
     the set's painted crown is lower than its box by 10/900 of the rendered
     height: 701.9 * 10/900 = 7.8.

     The stack is seated on the painted edge, not the box edge — otherwise the
     lowest tape floats on 7.8px of nothing. Kept as two named numbers rather
     than one baked constant so it stays obvious what the 7.8 is for if the
     TV asset is ever re-exported. */
  --tv-crown: 196;
  --tv-alpha-trim: 7.8;
  --tape-base: calc(var(--tv-crown) - var(--tv-alpha-trim));

  /* Screen opening measured off TV2.webp (912x900) as a % of the TV box */
  --screen-x: 13.816%;
  --screen-y: 12.889%;
  --screen-w: 73.026%;
  --screen-h: 51.333%;

  /* ======================================================================
     Motion
     ======================================================================
     Two families, deliberately different.

     1. CONTINUOUS — used for opacity and colour. These read as light and
        atmosphere, not as authored movement, so they interpolate normally.
     2. STEPPED — used for authored object motion. The site's motion language
        samples an eased curve at ~24 poses per second. Mirrored by FPS in
        js/motion.js; the two must be changed together.

     The step tokens are duration/pose PAIRS. The pose count is derived from
     the duration, never fixed at an arbitrary steps(n) — that is the whole
     point. Always use a --dur-X with its matching --step-X.

     Named by role rather than by pose count, because the count is a function
     of the cadence: at 12fps these were 2/3/4/6 and at 24fps they are 4/6/8/12
     for exactly the same durations. Baking the number into the token name
     would mean renaming every call site each time the cadence is retuned.

        --step-accent  @ 167ms    4 poses   quick hover accents
        --step-hover   @ 250ms    6 poses   standard hover / press
        --step-swap    @ 333ms    8 poses   label swaps, small reveals
        --step-enter   @ 500ms   12 poses   entrances, larger objects

     `jump-start` puts the first pose at t=0, so a hover is visibly
     acknowledged on the same frame the pointer arrives; the remaining poses
     carry the movement. `jump-end` is for entrances, where starting from the
     origin state is correct.
     ---------------------------------------------------------------------- */
  --ease-out:    cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease:        ease;

  --dur-accent: 167ms;  --step-accent: steps(4,  jump-start);
  --dur-hover:  250ms;  --step-hover:  steps(6,  jump-start);
  --dur-swap:   333ms;  --step-swap:   steps(8,  jump-start);
  --dur-enter:  500ms;  --step-enter:  steps(12, jump-start);

  --enter-swap:  steps(8,  jump-end);
  --enter-large: steps(12, jump-end);

  /* Continuous durations — opacity and atmosphere only. */
  --dur-fade:  180ms;
  --dur-glow:  450ms;

  /* --- Layers ----------------------------------------------------------
     One ladder, ten apart, named. Nothing in this project may invent a
     z-index outside it.
     ---------------------------------------------------------------------- */
  --z-room:       0;    /* depth planes: background, TV, tapes, foreground */
  --z-chrome:    30;    /* masthead, nav, readout */
  --z-signal:    50;    /* VHS/CRT transition surface */
  --z-loader:    60;    /* loading card */
  --z-info:      80;    /* info card dialog — under the cursor, over the loader */
  --z-cursor:    90;    /* dot-trail cursor canvas */
  --z-critical: 100;    /* skip link and anything that must outrank all */
  --z-transition: 200;  /* tape-selection overlay — lives on <body>, OUTSIDE
                           #screen's chroma filter (for performance), so it needs
                           its own rung above the cursor and chrome. It covers
                           the cursor while it runs, which is intended. */
}

/* Scene planes get their own small ladder inside --z-room. */
:root {
  --z-plane-bg:    1;
  --z-plane-tv:    2;
  --z-plane-tapes: 3;
  --z-plane-fore:  4;
}
