/* ═══════════════════════════════════════════════════════════════════════════
   tokens.css — Media Forest design system.  DARKROOM.

   Rewritten 2026-08-19 as phase 2 of the retheme. This file is the whole
   system: palette, type, spacing, rules, elevation, motion, and the page base.
   app.css is structure, darkroom.css is the design layer applied on top of it,
   adultswim.css is the one alternate theme. Load order:

       tokens.css → app.css → darkroom.css → adultswim.css

   ── The direction ────────────────────────────────────────────────────────
   The library as a film archive. Safelight amber on wet-plate black, cold blue
   for anything that is data, gelatin-silver for type. Every colour here comes
   out of that room, which is why the palette can carry meaning instead of just
   being applied:

       ground      the room            04151F
       tray        panels, wells       183A37
       safelight   the one accent      A9714B
       lighttable  data, links         7C9EB2
       gelatin     type, rules         B3BFB8

   ── What changed, and why ────────────────────────────────────────────────
   The 19 Aug study measured the old build: 98.7% of pixels sat below 6%
   relative luminance, 99.6% of its chroma was a single hue, and the accent was
   0.16% of the page. Three structural answers, all in this file:

   1. A REAL VALUE LADDER. The surface ramp climbs 0.66% → 5.0% luminance, and
      — the part that was missing — there is now a top end: --surface-paper at
      50% and --surface-paper-hi at 81%, for inverted chips, primary buttons
      and label plates. A dark interface still needs light somewhere or every
      element reads as the same distance from the viewer.

   2. TEXT COLOURS ARE SOLID, NOT ALPHA. The old file set secondary and muted
      as rgba() over an unknown backdrop, so their real contrast depended on
      whatever surface they landed on and could not be reasoned about. Every
      value below is measured against the surfaces it is actually used on;
      the numbers are in the comments and they are AA at minimum.

   3. ACCENTS HAVE A GRAPHIC CUT AND A TEXT CUT. --mf-safelight (A9714B) is
      3.03:1 on a tray panel — fine for a fill, a rule or a lamp, and a
      failure as small text. That is why --safelight-text exists and is a
      different value. Same for the light table. Using one accent value for
      both jobs is what made the old accent quietly inaccessible.

   ── Rules that hold ──────────────────────────────────────────────────────
   • Status colours (--status-*) are semantic and are NEVER themed. Mood lives
     in the accents. This survived from the previous file and was right.
   • Every downstream var() reads from the primitives, so a theme touches only
     its own block — no per-component overrides.
   • The compatibility aliases at the bottom of :root map ~60 legacy names onto
     the system. They exist so app.css did not need a flag-day rename. New code
     uses the real names.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Typefaces ────────────────────────────────────────────────────────────
   Three families, 88 KB total, all subset to Latin + the punctuation actually
   used. That is less than the single Fraunces file this replaces.

     Instrument Serif   display. One weight, high contrast, and an italic that
                        is the whole personality — one word per headline, never
                        the line. It is already a display cut, which is why it
                        does not need (and does not have) an optical-size axis:
                        see --fvs-display below.
     Instrument Sans    interface. Variable weight, slightly condensed, quiet.
     JetBrains Mono     data. Everything that is a measurement. Subset hard —
                        digits, caps, punctuation, ★ and · — to 31 KB.
   ─────────────────────────────────────────────────────────────────────── */
@font-face{
  font-family:"Instrument Serif";
  src:url("/static/fonts/instrument-serif-latin.woff2?v=1") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Instrument Serif";
  src:url("/static/fonts/instrument-serif-latin-italic.woff2?v=1") format("woff2");
  font-weight:400; font-style:italic; font-display:swap;
}
@font-face{
  font-family:"Instrument Sans";
  src:url("/static/fonts/instrument-sans-latin-var.woff2?v=1") format("woff2");
  font-weight:400 700; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"JetBrains Mono";
  src:url("/static/fonts/jetbrains-mono-latin-var.woff2?v=1") format("woff2");
  font-weight:400 700; font-style:normal; font-display:swap;
}
/* Kept for the Adult Swim theme and as a fallback for anything still asking
   for them. Not preloaded — nothing in the Darkroom theme uses them. */
@font-face{
  font-family:"InterVar";
  src:url("/static/fonts/inter-latin-var.woff2?v=1") format("woff2");
  font-weight:400 700; font-style:normal; font-display:swap;
}

:root{
  color-scheme:dark;

  /* ═══ THE FIVE ══════════════════════════════════════════════════════════
     Named for the room, not for their role, because their role changes per
     component and their identity does not. brand.css assets paint through
     these same names with literal fallbacks, so an SVG loaded through <img>
     — where there is no cascade — still renders in palette. */
  --mf-ground:#04151f;
  --mf-tray:#183a37;
  --mf-safelight:#a9714b;
  --mf-lighttable:#7c9eb2;
  --mf-gelatin:#b3bfb8;

  /* ═══ SURFACE RAMP ═════════════════════════════════════════════════════
     Six steps from the room to the tray, then the top end. Luminance in the
     comment — the ladder is the point, so it is written down.

     The paper steps are what fixes finding F2. They are not a background for
     the page; they are for the handful of elements that should read as LIT:
     the primary button, a selected chip, a label plate, the scrubber head.
     Used sparingly they give the page a top end. Used widely they turn it
     into a light theme. */
  --color-bg-deepest:#04151f;   /* 0.66% — the page field                  */
  --color-bg-deep:#071c25;      /* 1.01% — wells, inset areas              */
  --color-bg-surface:#0b242a;   /* 1.50% — panels, cards, the topbar slab  */
  --color-bg-elevated:#12302f;  /* 2.45% — raised panels, menus            */
  --color-bg-hover:#1f4640;     /* 5.04% — hover / active rows             */
  --surface-paper:#b3bfb8;      /* 50.3% — inverted: primary fill, plates  */
  --surface-paper-hi:#e4eae5;   /* 81.0% — inverted, pressed / selected    */
  /* Ink for anything sitting ON the paper steps. 9.77:1 on --surface-paper. */
  --on-paper:#04151f;

  /* ═══ ACCENTS ══════════════════════════════════════════════════════════
     Held under the historical --aurora-* names so every downstream reference
     (glows, sheen, service hues, badges) recolours for free.

       green-slot   → safelight   the one warm accent
       teal-slot    → light table  data, links, queued state
       purple-slot  → dim sage     tertiary, anime badge
       amber-slot   → print gold   the metal thread
       rose         → warm shadow  ambient only

     GRAPHIC vs TEXT cut. --aurora-green (A9714B) measures 4.55:1 on the page
     field but only 3.03:1 on a tray panel: correct for a fill, a rule or a
     lamp, a failure as small text. --safelight-text is the cut to use whenever
     the accent IS type. Same split on the light table. */
  --aurora-green:#a9714b;        --aurora-green-hover:#c8905f; --aurora-green-dim:#7a4f31;
  --aurora-teal:#7c9eb2;         --aurora-teal-hover:#9cb8c9;  --aurora-teal-dim:#55707f;
  --aurora-purple:#8a9a96;       --aurora-purple-hover:#a3b1ad;--aurora-purple-dim:#5f6d69;
  --aurora-amber:#c08a53;        --aurora-amber-hover:#d7a570;
  --aurora-rose:#8c5a44;

  /* the text cuts — 4.80:1 and 4.99:1 on --color-bg-hover's neighbour, the
     tray; both comfortably AA on every surface above */
  --safelight-text:#d1946a;
  --lighttable-text:#8aa9bc;

  /* The accent as a MATERIAL rather than a swatch. A flat fill of a single
     mid-tone is what an unstyled control looks like; the same colour with a
     top-to-bottom fall and a catch-line along its upper edge reads as an
     object with a light on it. This is the whole difference between the
     primary button looking machined and looking like a default. */
  /* The ramp is set by its DARKEST stop, not its prettiest one. A gradient
     button's text sits across the whole fall, so the bottom stop is what
     decides whether the label is readable — the first ramp measured 3.96:1
     there while looking fine at the top. These clear AA at every stop against
     --brass-ink: 8.56 / 6.61 / 5.25, and 9.96 / 7.74 / 6.21 on hover. */
  --brass-ink:#140a04;
  --brass:linear-gradient(180deg,#d9a06d 0%,#c38a5c 46%,#b0784f 100%);
  --brass-hover:linear-gradient(180deg,#e6ae7b 0%,#d0976a 46%,#bd855c 100%);
  --brass-edge:inset 0 1px 0 rgba(255,238,220,.38);
  --brass-shadow:0 1px 2px rgba(0,0,0,.5), 0 6px 16px -6px rgba(120,66,32,.5);

  --accent-base:var(--aurora-green);
  --accent-hover:var(--aurora-green-hover);
  --accent-dim:var(--aurora-green-dim);
  --accent-glow:color-mix(in srgb,var(--aurora-green) 20%,transparent);
  --accent-on:#180d06;

  /* ── Service hues ─────────────────────────────────────────────────────── */
  --amber-base:var(--aurora-amber); --amber-hover:var(--aurora-amber-hover); --amber-dim:#8a6238;
  --teal-base:var(--aurora-teal); --teal-hover:var(--aurora-teal-hover); --teal-dim:var(--aurora-teal-dim);
  --teal-glow:color-mix(in srgb,var(--aurora-teal) 22%,transparent);
  --purple-base:var(--aurora-purple); --purple-hover:var(--aurora-purple-hover); --purple-dim:var(--aurora-purple-dim);

  /* ═══ TEXT ═════════════════════════════════════════════════════════════
     Solid values, not alpha. Contrast measured against the page field / a
     panel / a tray panel:

       primary    15.51 / 13.51 / 10.33
       secondary   9.31 /  8.11 /  6.20
       muted       7.14 /  6.22 /  4.76
       faint       4.28 /  3.73 /  2.85   ← AA-fail by design, see below

     --text-faint is for text that is genuinely decorative or duplicated
     elsewhere (a watermark, a repeated count). It must never carry the only
     copy of a fact. It exists so that "quieter than muted" has a named value
     instead of every rule inventing its own opacity. */
  --text-primary:#e7ece8;
  --text-secondary:#aebbb5;
  --text-muted:#95a49f;
  /* Raised from #6b7a75. The old value measured 3.59:1 on `--color-bg-surface`
     — below AA — which was invisible for as long as the ramp's bottom step was
     only ever used on decoration. The moment it started carrying the mono
     capitals that label every readout on the site, it became six real failures
     in the instrument alone. 4.7:1 at worst, on `--color-bg-elevated`. */
  --text-faint:#8a9892;

  /* ═══ RULES ════════════════════════════════════════════════════════════
     Depth in this theme comes from drawn rules, not from shadow. Four weights,
     and they are the vocabulary — a component picks one rather than inventing
     an rgba. */
  --border-subtle:rgba(179,191,184,0.09);
  --border-default:rgba(179,191,184,0.16);
  --border-strong:rgba(179,191,184,0.28);
  --border-accent:color-mix(in srgb,var(--aurora-green) 55%,transparent);
  --rule-hair:1px solid var(--border-subtle);
  --rule:1px solid var(--border-default);
  --rule-strong:1px solid var(--border-strong);
  --rule-accent:1px solid var(--border-accent);

  /* ═══ STATUS — semantic, never themed ══════════════════════════════════
     These four are the only colours on the site that carry information rather
     than mood: service up/degraded/down, the uptime bars, the live pulse, the
     error text. Pointing them at the accent primitives (which every theme
     remaps) is how the previous build collapsed the separation between "a
     healthy service" and "a dead one" to Δ36. Themes must not override them.
     Contrast on the page field: 11.81 / 10.51 / 6.57 / 9.60. */
  --status-success:#4de8a0;
  --status-warning:#f5b942;
  --status-danger:#f4716f;
  --status-info:#5bc8e8;

  /* ═══ FOCUS ════════════════════════════════════════════════════════════
     Full-strength safelight, not a mix. A focus ring is the one place where
     "subtle" is the wrong instinct — it has to win against whatever it lands
     on, including poster art. */
  --focus-ring:#d1946a;

  /* ═══ TYPE ═════════════════════════════════════════════════════════════ */
  --font-serif:"Instrument Serif","Fraunces","Hoefler Text",Cambria,Georgia,serif;
  --font-sans:"Instrument Sans","InterVar",Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --font-mono:"JetBrains Mono",ui-monospace,"SF Mono","Cascadia Code",Consolas,monospace;

  /* The scale, unchanged from the 18 Aug rebuild — it was right. One monotonic
     run, 11 12 13 14 15 16 18 20 24 30 38 52, tightly stepped where text is
     read (≈1.08x) and widening toward display (≈1.37x), which is where
     contrast should live. */
  --text-3xs:0.6875rem;   /* 11 — micro labels, the legibility floor */
  --text-2xs:0.75rem;     /* 12 */
  --text-xs:0.8125rem;    /* 13 */
  --text-sm:0.875rem;     /* 14 */
  --text-body:0.9375rem;  /* 15 */
  --text-base:1rem;       /* 16 — body */
  --text-md:1.125rem;     /* 18 */
  --text-lg:1.25rem;      /* 20 */
  --text-xl:1.5rem;       /* 24 */
  --text-2xl:1.875rem;    /* 30 */
  --text-3xl:2.375rem;    /* 38 */
  --text-4xl:3.25rem;     /* 52 */
  --text-caption:var(--text-2xs);
  --text-detail:var(--text-xs);

  /* Display sizes, opened up. The study's F7: nothing on the old page was set
     at a size that would make anyone look twice. Instrument Serif is a display
     cut and holds together far larger than Fraunces did. */
  --head-rail:clamp(1.6rem,2.6vw,2.35rem);
  --head-card:1.25rem;
  --head-hero:clamp(2.2rem,5.2vw,4.4rem);
  --head-modal:clamp(1.9rem,3.6vw,2.7rem);
  --head-sheet:1.85rem;
  --head-page:clamp(2.8rem,7vw,5rem);
  --head-display:clamp(3.4rem,9.5vw,7rem);

  --fw-regular:400; --fw-medium:500; --fw-semibold:600; --fw-bold:700;
  --lh-tight:1.02; --lh-snug:1.22; --lh-normal:1.58; --lh-relaxed:1.72;

  /* Tracking runs INVERSELY to size: large type is optically over-spaced at
     its default fit, small caps are under-spaced. Five steps, the whole
     vocabulary. */
  --track-display:-0.022em;  /* 30px and up */
  --track-tight:-0.011em;    /* 18–24px headings */
  --track-normal:0;
  --track-caps:0.11em;       /* uppercase labels at body size */
  --track-caps-sm:0.18em;    /* uppercase micro-labels, 11–12px */
  --track-data:0.14em;       /* the mono readouts */

  /* Instrument Serif is a static font with no variable axes — it is already
     drawn as a display cut, which is exactly why it does not need one. This
     token stays because app.css applies it by name to every display element,
     and because the Adult Swim block and any future variable face will want
     it. `normal` is a valid, no-op value. */
  --fvs-display:normal;
  --display-tracking:var(--track-display);

  /* Standalone-page voice bridge — wall/journey/request load only this file,
     so their display voice has to ride tokens. Theme blocks remap them. */
  --voice-weight:400; --voice-tracking:-0.018em; --voice-transform:none;

  /* ═══ SPACE ════════════════════════════════════════════════════════════ */
  --sp-1:0.25rem; --sp-2:0.5rem; --sp-3:0.75rem; --sp-4:1rem;
  --sp-5:1.25rem; --sp-6:1.5rem; --sp-8:2rem; --sp-10:2.5rem;
  --sp-12:3rem; --sp-16:4rem; --sp-20:5rem;
  --heading-gap:var(--sp-5);

  /* The hung margin column — the layout move that makes the page editorial.
     Plate numbers, timecodes and counts live out here in mono; content starts
     at the second column. Collapses to zero under 1100px, where the page has
     no width to spare. */
  --margin-col:0px;

  /* ═══ RADII ════════════════════════════════════════════════════════════
     Revised 2026-08-19 after seeing this on the real site: "too blocky and
     cheap feeling."

     The first cut reasoned that an archive is square — sleeves, contact
     sheets, slates, film gates — and set every radius to 0–3px. That is a
     correct observation about physical objects and the wrong conclusion about
     an interface. Zero radius plus a 1px hairline plus no shadow does not read
     as *considered*; it reads as *unfinished*, because it is also what an
     unstyled element looks like. Square is the Adult Swim register, and that
     theme keeps it (it overrides these back to 0–2px) — but Adult Swim is
     deliberately cheap-looking, which is the joke. Darkroom is not supposed to
     be.

     A 6–8px radius is the difference between a drawn rectangle and an object.
     Nothing here is a pill; nothing here is a blob. */
  --radius-xs:2px;
  --radius-sm:4px;
  --radius-md:6px;
  --radius-lg:8px;
  --radius-xl:12px;
  --radius-pill:9999px;

  /* ═══ ELEVATION ════════════════════════════════════════════════════════
     Also revised. The first cut removed every shadow on the grounds that the
     old build's 44px coloured halo was dated — which it was — and replaced it
     with nothing, so the whole page collapsed onto one plane. A page where
     nothing sits above anything else is a diagram, not an interface, and that
     flatness is the other half of "blocky".

     The distinction that matters is between a GLOW and LIGHT. A glow is a
     coloured bloom around an element and always looks cheap. Light is a
     catch-line along the top edge where a surface would face a lamp, plus a
     soft dark shadow beneath it where it would occlude the ground. These are
     the second kind: no colour in any of them, wide and very soft, and always
     paired with the edge highlight so a raised surface reads as lit rather
     than as outlined. */
  --edge-highlight:inset 0 1px 0 rgba(238,244,240,0.09);
  --edge-highlight-strong:inset 0 1px 0 rgba(238,244,240,0.14);
  --shadow-sm:0 1px 2px rgba(0,0,0,.5), 0 2px 6px -2px rgba(0,0,0,.4);
  --shadow-md:0 2px 4px rgba(0,0,0,.45), 0 10px 24px -8px rgba(0,0,0,.55);
  --shadow-lg:0 4px 8px rgba(0,0,0,.4), 0 20px 48px -12px rgba(0,0,0,.62);
  --shadow-xl:0 8px 16px rgba(0,0,0,.42), 0 36px 80px -20px rgba(0,0,0,.7);
  --shadow-raised:var(--shadow-sm), var(--edge-highlight);
  --shadow-modal:var(--shadow-xl), var(--edge-highlight), 0 0 0 1px rgba(0,0,0,.5);

  /* ═══ SURFACES ═════════════════════════════════════════════════════════
     Every backdrop-filter in the theme is off. The surfaces here are opaque
     equipment, so a blur pass would be invisible and still cost a full
     backdrop re-sample per scrolled frame — the same finding the 17 Aug pass
     made for Adult Swim, now true of the default theme as well. The tokens
     stay so that the rules reading them keep parsing. */
  --glass-card-bg:var(--color-bg-surface);
  --glass-header-bg:rgba(4,21,31,0.94);
  --glass-card-blur:none;
  --glass-header-blur:none;

  /* ═══ THE METAL ════════════════════════════════════════════════════════
     The old palette threaded champagne gilt through everything. Darkroom has
     no gilt; what it has is a thread of warm print gold on edges that should
     catch light, at a fraction of the old strength. Kept under the same names
     so the rules that use them do not need rewriting. */
  --gilt:color-mix(in srgb,var(--aurora-amber) 46%,transparent);
  --gilt-line:color-mix(in srgb,var(--aurora-amber) 24%,transparent);
  --gilt-soft:color-mix(in srgb,var(--aurora-amber) 10%,transparent);
  --gilt-glow:color-mix(in srgb,var(--aurora-amber) 30%,transparent);

  /* the ground, as one fixed gradient: a faint lift under the header falling
     to a deeper black at the foot. One paint; it is what every surface above
     it sits against. */
  --ground-gradient:linear-gradient(180deg,
    color-mix(in srgb,var(--color-bg-surface) 55%,var(--color-bg-deepest)) 0%,
    var(--color-bg-deepest) 42%,
    #020a0e 100%);

  /* The section mark. Was a four-point sparkle — decoration standing in for a
     hierarchy the type should carry. It is now the registration crosshair from
     the brand set, so the mark that appears beside every heading is the same
     mark that sits on the corner of featured artwork. Declared as a mask so it
     takes any colour. */
  --spark-mask:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%3E%3Cpath%20d%3D'M8%200v4.4M8%2011.6V16M0%208h4.4M11.6%208H16'%20stroke%3D'%23000'%20stroke-width%3D'1.4'%20fill%3D'none'%2F%3E%3Ccircle%20cx%3D'8'%20cy%3D'8'%20r%3D'3.2'%20stroke%3D'%23000'%20stroke-width%3D'1.4'%20fill%3D'none'%2F%3E%3C%2Fsvg%3E");

  /* The sprocket rule — the divider. One 20px tile, rastered once and repeated
     however many dividers the page has. */
  --sprocket-tile:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2020%2014'%20width%3D'20'%20height%3D'14'%3E%3Crect%20x%3D'6.5'%20y%3D'2.5'%20width%3D'7'%20height%3D'9'%20rx%3D'1.2'%20fill%3D'%23a9714b'%2F%3E%3C%2Fsvg%3E");
  /* Baked 128px noise, ~4%. Not feTurbulence, not mix-blend-mode: a blend
     layer keeps everything beneath it as a separate compositing group and
     costs a full-viewport pass every frame even at zero opacity. Applied to
     surfaces, never to body. */
  --grain-tile:url("/static/brand/grain.png");

  /* ═══ MOTION ═══════════════════════════════════════════════════════════
     Three system behaviours carry this theme — the shutter (view change), the
     develop (image arrival) and the stagger (rail entrance) — plus ordinary
     state feedback. The durations are the 18 Aug scale, which was right; the
     easing is new.

     --ease-shutter is the one with an opinion: a hard in, a hard out, and no
     dwell in the middle. It is what makes a transition read as a mechanism
     rather than a fade. */
  --dur-instant:80ms;   /* press, ripple            */
  --dur-fast:140ms;     /* hover, colour, borders   */
  --dur-base:200ms;     /* transforms, reveals      */
  --dur-slow:320ms;     /* entrances, overlays      */
  --dur-slower:480ms;   /* once-per-view moments    */

  --ease-standard:cubic-bezier(0.2,0.7,0.2,1);
  --ease-enter:cubic-bezier(0.16,1,0.3,1);
  --ease-exit:cubic-bezier(0.4,0,1,1);
  --ease-shutter:cubic-bezier(0.85,0,0.15,1);
  --ease-spring:cubic-bezier(0.34,1.4,0.64,1);

  /* Legacy motion names — same values, kept so old rules resolve. */
  --motion-instant:var(--dur-instant); --motion-fast:var(--dur-fast);
  --motion-normal:var(--dur-base); --motion-slow:var(--dur-slow);
  --motion-glacial:var(--dur-slower);

  /* ═══ CARDS ════════════════════════════════════════════════════════════
     A card in this theme is a frame around artwork, not a floating slab: a
     hairline, a square corner, and a lift measured in single pixels. */
  --card-radius:var(--radius-lg);
  --card-bg:var(--color-bg-surface);
  --card-blur:none;
  --card-border:1px solid var(--border-default);
  --card-shadow-rest:var(--shadow-sm), var(--edge-highlight);
  --card-shadow-hover:var(--shadow-lg), var(--edge-highlight-strong);
  --card-lift:translateY(-5px);
  --card-lift-sm:translateY(-2px);

  /* ═══ LAYOUT ═══════════════════════════════════════════════════════════ */
  --w-wide:min(94vw,1680px);
  --w-read:min(92vw,1080px);
  --card-w:clamp(158px,13vw,240px);
  --gutter:var(--sp-6);
  --section:var(--sp-16);
  --bleed-inset:max(var(--gutter),calc((100% - var(--w-wide))/2 + var(--gutter)));
  --topbar-h:64px;

  /* ═══════════════ COMPATIBILITY ALIASES — legacy → system ═══════════════
     ~60 names app.css still consumes. They are one-line indirections, they
     cost nothing at runtime, and they are why this rewrite did not need to
     touch 3,600 lines of component CSS. New code uses the names above. */
  --void:var(--color-bg-deepest); --bg2:var(--color-bg-deep);
  --surface:var(--color-bg-surface); --raised:var(--color-bg-elevated); --raised-2:var(--color-bg-hover);
  --raised-bg:var(--color-bg-elevated); --panel-bg:var(--color-bg-surface);
  --modal-grad-2:var(--color-bg-deep);
  --bg:var(--color-bg-deepest); --bg-soft:var(--color-bg-surface);
  /* Accent triad */
  --magenta:var(--accent-base); --violet:var(--teal-base); --cyan:var(--accent-hover);
  --accent:var(--accent-base); --accent-2:var(--teal-base);
  --gm:var(--accent-glow); --gc:var(--teal-glow);
  --neon:var(--accent-base);
  --grad-progress:var(--accent-base);
  /* Text */
  --platinum:var(--text-primary); --platinum-bright:var(--text-primary);
  --ink:var(--text-primary); --ink-dim:var(--text-secondary); --muted:var(--text-muted);
  --star:var(--aurora-amber-hover);
  /* Links */
  --link:var(--safelight-text); --link-2:var(--lighttable-text);
  /* Borders */
  --line:var(--border-default); --line-c:var(--text-muted); --line-strong:var(--border-strong);
  --track:var(--border-subtle);
  /* Status */
  --ok:var(--status-success); --ok-dim:var(--status-success);
  --down:var(--status-danger); --down-dim:var(--status-danger);
  --warn:var(--status-warning); --warn-dim:var(--status-warning);
  --badge-tv:var(--teal-base); --badge-anime:var(--purple-base);
  /* Easing */
  --ease:var(--ease-standard);
  /* Fonts */
  --sans:var(--font-sans); --serif:var(--font-serif);
  /* Radii */
  --r-sm:var(--radius-sm); --r-md:var(--radius-md); --r-lg:var(--radius-lg);
  --r-xl:var(--radius-xl); --r-pill:var(--radius-pill);
  /* Spacing */
  --s1:var(--sp-1); --s2:var(--sp-2); --s3:var(--sp-3); --s4:var(--sp-4);
  --s5:var(--sp-5); --s6:var(--sp-6); --s7:var(--sp-8); --s8:var(--sp-16);
  /* Type sizes */
  --t-body:var(--text-base); --t-meta:var(--text-xs); --t-cap:var(--text-xs);
  --t-eyebrow:var(--text-3xs); --t-lead:clamp(17px,2vw,20px);
  --ring-a:0deg;
}

/* Above 1100px the page gets its hung margin column. Below it there is no
   width to spare and the metadata folds back inline. */
@media(min-width:1100px){ :root{ --margin-col:5.5rem; } }


/* ═══════════════════════════════════════════════════════════════════════════
   THEME ROSTER

   Two themes. Darkroom (the :root palette above) is the default; Adult Swim is
   the alternate. Nightshade, Autumn and Bronze were retired on 19 Aug — five
   themes meant five half-finished designs and no finished one, and every hour
   spent keeping them in sync was an hour not spent on the one people see.

   Adding a theme = one block here + one entry in app.js THEMES[] + the VALID[]
   list in index.html's pre-paint script. All three must agree or the pre-paint
   guard silently resets the visitor to default.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Adult Swim — old-school bumper. White on pure black, broadcast-grade: safety
   orange lead, hazard yellow leaf, SMPTE cyan links. Display type is
   Impact-class condensed caps; the wordmark alone gets the lowercase
   [media|forest] homage (adultswim.css, scoped). The palette is deliberately
   limited, like a warning sign.

   Status colours stay literal per the semantic rule — they already ARE this
   palette (hospital green, hazard yellow, emergency red, broadcast cyan), so
   nothing here overrides them. */
html[data-theme="adultswim"]{
  --mf-ground:#000000; --mf-tray:#111111; --mf-safelight:#ff6600;
  --mf-lighttable:#33ccff; --mf-gelatin:#ffffff;

  --aurora-green:#ff6600; --aurora-green-hover:#ff8533; --aurora-green-dim:#b34700;
  --aurora-teal:#33ccff; --aurora-teal-hover:#66d9ff; --aurora-teal-dim:#1f7a99;
  --aurora-purple:#b0b0b0; --aurora-purple-hover:#cccccc; --aurora-purple-dim:#737373;
  --aurora-amber:#ffcc00; --aurora-amber-hover:#ffe14d;
  --aurora-rose:#d43d3d;
  /* Both accents already clear AA as text on dead black (5.9:1 and 9.6:1), so
     the graphic and text cuts are the same value here. The split exists
     because Darkroom's ground is lighter than this one, not as a rule. */
  --safelight-text:#ff8533; --lighttable-text:#33ccff;

  --accent-base:var(--aurora-green); --accent-hover:var(--aurora-green-hover); --accent-dim:var(--aurora-green-dim);
  --teal-base:var(--aurora-teal); --teal-hover:var(--aurora-teal-hover); --teal-dim:var(--aurora-teal-dim);
  --purple-base:var(--aurora-purple); --purple-hover:var(--aurora-purple-hover); --purple-dim:var(--aurora-purple-dim);
  --amber-base:var(--aurora-amber); --amber-hover:var(--aurora-amber-hover); --amber-dim:#8f7300;
  --accent-glow:color-mix(in srgb,var(--aurora-green) 14%,transparent);
  --teal-glow:color-mix(in srgb,var(--aurora-teal) 16%,transparent);
  --accent-on:#000000;

  --color-bg-deepest:#000000; --color-bg-deep:#050505; --color-bg-surface:#0a0a0a;
  --color-bg-elevated:#111111; --color-bg-hover:#181818;
  --surface-paper:#ffffff; --surface-paper-hi:#ffffff; --on-paper:#000000;

  /* Near-opaque flat black over a dead-black field: a blur pass is invisible
     and still costs a full backdrop re-sample per scrolled frame. */
  --glass-card-bg:rgba(0,0,0,0.9); --glass-header-bg:rgba(0,0,0,0.97);
  --glass-card-blur:none; --glass-header-blur:none; --card-blur:none;

  --text-primary:#ffffff; --text-secondary:rgba(255,255,255,0.76);
  --text-muted:rgba(255,255,255,0.62); --text-faint:rgba(255,255,255,0.42);
  --border-subtle:rgba(255,255,255,0.08); --border-default:rgba(255,255,255,0.14);
  --border-strong:rgba(255,255,255,0.24); --border-accent:color-mix(in srgb,var(--aurora-green) 45%,transparent);

  /* Focus is hazard yellow, not the orange accent: orange reads as *warning*
     in this palette, not as *you are here*, and yellow is the better contrast
     pairing on black. */
  --focus-ring:#ffcc00;

  /* Type: condensed broadcast display. */
  --font-serif:'Impact','Haettenschweiler','Franklin Gothic Bold','Arial Black','Arial Narrow',Arial,sans-serif;
  --font-sans:"Instrument Sans","InterVar",Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;
  --fvs-display:normal; --display-tracking:0.015em;

  /* Radii: broadcast equipment has no soft corners (pill stays round). This
     block used to agree with :root; now that Darkroom has softened, the square
     is this theme's alone — which is as it should be, because "cheap
     broadcast plastic" is the joke here and the point of the other theme is
     that it is not a joke. */
  --radius-xs:0; --radius-sm:1px; --radius-md:2px; --radius-lg:2px; --radius-xl:2px;
  /* flat, hard, no material — the opposite of the brass */
  --brass:var(--aurora-green); --brass-hover:var(--aurora-green-hover);
  --brass-edge:none; --brass-shadow:0 2px 0 rgba(0,0,0,.9); --brass-ink:#000000;
  --edge-highlight-strong:inset 0 1px 0 rgba(255,255,255,0.10);
  --shadow-xl:0 0 0 1px rgba(255,255,255,0.12), 0 20px 44px -12px rgba(0,0,0,0.95);

  /* Shadows: harsh offset + hairline, never a soft colour glow. */
  --shadow-modal:0 0 0 1px rgba(255,255,255,0.12), 0 24px 60px -12px rgba(0,0,0,0.95);
  --shadow-raised:0 2px 0 rgba(0,0,0,0.9);
  --shadow-sm:0 0 0 1px rgba(0,0,0,0.6), 0 2px 0 rgba(0,0,0,0.8);
  --shadow-md:0 0 0 1px rgba(255,255,255,0.08), 0 6px 16px -6px rgba(0,0,0,0.9);
  --shadow-lg:0 0 0 1px rgba(255,255,255,0.12), 0 14px 34px -10px rgba(0,0,0,0.95);
  --edge-highlight:inset 0 1px 0 rgba(255,255,255,0.07);
  --card-shadow-rest:0 1px 0 rgba(255,255,255,0.05), 0 8px 20px -8px rgba(0,0,0,0.9);
  --card-shadow-hover:0 0 0 1px var(--border-strong), 0 16px 32px -10px rgba(0,0,0,0.95);

  /* Easing: hard steps, no glide. Cuts, not glides.
     The duration compression matters as much as the curve: steps(2) over 400ms
     renders as two discrete jumps 200ms apart, which is frame-for-frame
     indistinguishable from a stalled page. At 140ms the same curve reads as a
     broadcast switcher snap. */
  --ease-standard:steps(2); --ease-enter:steps(3); --ease-spring:steps(2);
  --ease-shutter:steps(2); --ease-exit:cubic-bezier(0.9,0,1,1);

  /* No sprocket, no grain — this theme's texture is the CRT line pattern in
     adultswim.css, and its divider is the hazard rule. */
  --sprocket-tile:none;
  --grain-tile:none;
  --ground-gradient:none;

  --voice-weight:600; --voice-tracking:.06em; --voice-transform:uppercase;
}


/* ═══════════════════════════════════════════════════════════════════════════
   PAGE BASE
   ═══════════════════════════════════════════════════════════════════════════ */
*,*::before,*::after{box-sizing:border-box}
@media(prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
html,body{margin:0;padding:0}
body{
  min-height:100vh;
  /* The ground was a single flat fill, so the page had no top and no bottom
     and no sense of where the light was. This is one fixed gradient — a faint
     lift under the header falling to a deeper black at the foot — which costs
     one paint and gives every surface above it something to sit against. */
  background-color:var(--color-bg-deepest);
  background-image:var(--ground-gradient);
  background-attachment:fixed;
  color:var(--text-primary);
  font-family:var(--font-sans);
  font-size:var(--text-base);
  line-height:var(--lh-normal);
  text-rendering:optimizeLegibility;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
  scrollbar-width:thin; scrollbar-color:var(--border-strong) transparent;
}
/* Display treatment, applied to the family rather than per heading. Instrument
   Serif has no variable axes, so --fvs-display is a no-op under Darkroom and
   carries weight only for a theme that swaps in a variable face. The tracking
   is the part that matters: a display serif set at its default fit is
   optically loose at 40px and up. */
.wordmark, .slice h2, .railhead .cap, .gate-title, .libtitle, .ltitle,
.hero-title, .instrument-title, .together-title, .sg-title{
  font-variation-settings:var(--fvs-display);
  letter-spacing:var(--display-tracking);
}
body::-webkit-scrollbar{width:8px; height:8px;}
body::-webkit-scrollbar-track{background:transparent;}
body::-webkit-scrollbar-thumb{background:var(--border-strong); border-radius:0;}
body::-webkit-scrollbar-thumb:hover{background:var(--accent-base);}
body.locked{overflow:hidden}
::selection{background:var(--accent-base); color:var(--accent-on);}
input,textarea,select{caret-color:var(--accent-base);}

/* ── The safelight ────────────────────────────────────────────────────────
   The one backdrop, and it replaced the ambient aurora — which was the direct
   cause of finding F1, a single hue washed over the entire page on two
   permanently-running 50-second animations.

   What is here instead is a lamp: one warm bloom high on the right where a
   safelight would hang, and a cold pool along the bottom edge where a light
   table would sit. Static. No animation, no filter, no blend mode — two
   radial gradients on one fixed element, painted once. */
.aurora-sky{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(ellipse 62% 46% at 82% -8%,
      color-mix(in srgb,var(--aurora-green) 22%,transparent), transparent 60%),
    radial-gradient(ellipse 46% 34% at 12% 4%,
      color-mix(in srgb,var(--aurora-teal) 10%,transparent), transparent 64%),
    radial-gradient(ellipse 96% 26% at 50% 104%,
      color-mix(in srgb,var(--aurora-teal) 7%,transparent), transparent 70%);
}

/* ── Grain ────────────────────────────────────────────────────────────────
   The old grain layer was a viewport-sized feTurbulence tile at
   mix-blend-mode:soft-light. A blend mode keeps everything beneath it as a
   separate compositing group and costs a full-viewport blend pass every frame,
   which is what the 17 Aug pass measured and had to switch off entirely under
   Adult Swim. This is the same effect as a baked 128px PNG at 4%: one decode,
   one tiled paint, no compositing group, no blend mode. */
body::before{
  content:"";position:fixed;inset:0;z-index:1;pointer-events:none;
  background-image:var(--grain-tile);
  background-repeat:repeat;
}

/* ── Cursor safelight ─────────────────────────────────────────────────────
   Kept, and given a job. It is small and warm now — a lamp being carried past
   the shelf rather than a 500px coloured wash — and it is what lifts the
   sheen off the sprocket rules and hairlines as it passes. Compositor-only:
   app.js moves it with translate3d and never touches left/top. */
.cursor-glow{
  position:fixed;pointer-events:none;z-index:5;
  width:340px;height:340px;border-radius:50%;
  background:radial-gradient(circle,
    color-mix(in srgb,var(--aurora-green) 9%,transparent),
    color-mix(in srgb,var(--aurora-green) 3%,transparent) 45%,
    transparent 68%);
  transform:translate(-50%,-50%);opacity:0;
  transition:opacity var(--dur-slow) var(--ease-standard);
}
.cursor-glow.active{opacity:1}

/* ── Skeleton loading ─────────────────────────────────────────────────────
   No shimmer. A shimmer animates background-position, which is paint-only and
   fires exactly when the main thread is busy decoding the images it is
   standing in for. An unexposed frame is a flat slate with a hairline, and
   that is both cheaper and more honest about what is happening. */
.skeleton{
  background:var(--color-bg-deep);
  box-shadow:inset 0 0 0 1px var(--border-subtle);
  border-radius:var(--radius-md);
}

/* ── Theme cross-fade ─────────────────────────────────────────────────────
   Applied only while switching (html.theming, toggled for ~450ms by
   applyTheme in app.js). Scoped to the properties and elements that actually
   carry colour — the previous version put a 420ms transition on background,
   color, border, box-shadow AND text-shadow for every element and pseudo on
   the page, which is a universal-selector repaint storm and was the single
   most expensive moment in the app. */
html.theming body, html.theming #topbar, html.theming .slice,
html.theming .pcard, html.theming .poster, html.theming .cwcard,
html.theming .npc, html.theming .btn, html.theming .lbtn,
html.theming .railhead .cap, html.theming .foot{
  transition:background-color var(--dur-slow) var(--ease-standard),
             color var(--dur-slow) var(--ease-standard),
             border-color var(--dur-slow) var(--ease-standard) !important;
}

/* ── Sheen ────────────────────────────────────────────────────────────────
   Was a four-stop gradient clipped to the text of the wordmark and the gate
   headline, animated on background-position forever. Gradient-filled display
   text is the most reliable way to make a page look like a 2019 template, and
   the animation was repainting a glyph run on an idle page. Flat now — the
   class survives because markup and the forced-colors fallback both reference
   it. */
.sheen{ color:var(--text-primary); }

@media(prefers-reduced-motion:reduce){
  .cursor-glow{display:none!important}
  html.theming body, html.theming #topbar, html.theming .slice,
  html.theming .pcard, html.theming .poster, html.theming .cwcard,
  html.theming .npc, html.theming .btn, html.theming .lbtn,
  html.theming .railhead .cap, html.theming .foot{transition:none!important}
  /* Standalone member pages load only this file, so their entrance / pulse /
     step motion has to be killed here: wall + request card rise, wall live
     dot, journey stepper. */
  .card{animation:none!important}
  .live-dot i{animation:none!important}
  .step .dot{transition:none!important}
}
