/* lair.css — THE LAIR SITE THEME.
   =========================================================================
   A whole-site look, selected by one manifest key: `web.site.theme`. Loaded
   after terminal.css (the house look) and soundchrome.css, before the owner's
   own overlay. See assemble.py::site_theme for the switch, and the seven
   sheets this one sits beside (instrument / fracture / passage / signal /
   hatch / rift / aerosol).

   WHY IT EXISTS (Dino Queen's Lair, 2026-08-24, Ishaq's own words): "their
   website should be glitch style too. animations, etc. and not terminal.
   make it unique and cool ashell" — on top of the original brief: "make
   design very proffesional, advanced, smooth, good sounds, and good
   transitions. heres the logo and background so use them."

   THE TWO PICTURES ARE THE BRIEF (queenlairlogo.png / queenlairbackground.png,
   MEASURED 2026-08-24 — the first build guessed and shipped fabricated art;
   this is the correction). The owner's crest is an ELECTRIC "Q" of lightning:
   a crimson arc on the left, a cobalt arc on the right, white plasma veins,
   magenta bleeding through the seam — a red<->blue chromatic split made
   literal. The scene behind it is a BLACK grunge wall: a red chain and a blue
   chain crossing, a dripping magenta graffiti CROWN (the Queen), spray-paint
   splatter and X-marks. Every colour below is a token measured off those files
   (theme.palette in owner.json): blue ($accent, the right arc / blue chain),
   magenta ($primary, the crown — every committing control), red (--lr-red, the
   left arc, warm half of the tear), on a page ground that is the wall's near-
   black. bad-red is danger only, kept off the commit paint.

   THESIS, STATED ONCE, BECAUSE EVERY RULE HERE ANSWERS TO IT:
   THE PAGE IS A CHARGED WALL. His crest is high-voltage graffiti on black;
   every plate is dark glass with a faint charge from below, and the GLITCH is
   the one live-state motif — the signal tearing red-from-blue like the logo's
   own arc. Nothing here is a terminal: no ruled ground, no prompt furniture,
   no mono body copy.

   OWN-WORLD — what is this theme and not one of the other seven, each
   checkable in a screenshot:
     1. THE TEAR RULE. A region heading is announced by TEARING: its two
        clipped slivers slip sideways for a beat in the two paints (a
        chromatic split), then land. No sibling announces a region this way —
        aerosol names it in a full-bleed hollow strip, rift hangs it off a
        spine, signal frames it. Here the heading ITSELF is the event.
        The base state is a finished, legible heading; the tear is layered
        over it and stops entirely under prefers-reduced-motion.
     2. THE DROPOUT HAIRLINE. Section dividers and live edges are a scanline
        whose SIGNAL DROPS OUT: a travelling gap walks the line. The inverse
        of aerosol's crackle (their dash TRAVELS; my LINE is continuous and
        the HOLE travels) — a transmission losing a frame, not paint
        cracking.
     3. GLASS ON THE WALL. Every plate is translucent near-black glass with a
        faint charge from below and a 6px radius: deliberately NOT aerosol's
        stencil zero and NOT the house's full set.
     4. TWO PAINTS, ONE JOB EACH, AND THE ARITHMETIC THAT FORBIDS SWAPPING
        THEM (measured, this owner's palette):
            blue     on bg ............ 4.44:1   <- MARK (large/live only)
            magenta  on bg ............ 5.79:1
            fg       on magenta ....... 3.29:1   <- forbidden as ink
            bg-deep  on magenta ....... 5.49:1   <- the ONLY ink on a slab
            bg-deep  on blue .......... 4.57:1
            bad-red  on bg ............ 5.33:1   <- danger = crest crimson, off commit
        BLUE MARKS (headings, live state, the current page, links). MAGENTA
        (the crown) COMMITS — every committing control is a filled magenta
        slab. The only ink on EITHER paint is the deep ground token. Danger
        stays the house's filled bad plate with its icon and its word — bad-red
        differs from magenta in HUE and from blue in HUE and LIGHTNESS, the
        two-reds law's requirement, and red is otherwise reserved for the tear.
     5. THE SPLIT SIGNATURE. The brand name and each page's h1 carry a
        permanent 1px chromatic split (blue up-left, RED down-right — the
        crest's own arc, NOT the magenta commit paint) as
        TEXT-SHADOW on a SOLID fill. Deliberately not -webkit-text-stroke and
        deliberately never a transparent fill: a stroke in currentColor on
        transparent text renders INVISIBLE and shipped as an empty band once
        (feedback_a_text_stroke_in_currentcolor_on_transparent_text_is
        _invisible). The fill here is always the full ink token; losing the
        shadows loses only the flourish.
     6. FIGURES ARE MONUMENTS. Counts and prices take the display face at up
        to 58px; body copy never passes 15px; labels are 10.5px caps with
        wide tracking in the ui face — NOT mono ("not terminal" is the ask):
        the mono face survives only where a value IS data (codes, ids,
        tabular numerals).

   MOTION: CSS FIRST AND COMPLETE WITHOUT THE SCRIPT. The tear fires once on
   hover/entrance from this sheet alone; js/lair.js only ever ADDS `.lr-torn`
   bursts to headings the sheet already styles and the soft UI sounds — a
   blocked script leaves a finished page. EVERY animated property is
   transform, opacity, clip-path, background-position or a mask position, and
   every one of them stops under prefers-reduced-motion.

   THE GROUND CARRIES NO RULED GEOMETRY (test_house_look.py's law): every
   repeating paint here lives on a SECTION or a pane, never on html/body/
   main/.night or their pseudo-elements.

   NO RAW COLOUR LITERALS: every colour is a var() off the owner's palette.
   Local tokens wear the two letters l-r. Where the house look out-specifies
   something, the SELECTOR is raised — never a hammered priority.

   AND NEVER WRITE A STAR AND A SLASH TOGETHER IN THIS PROSE. Adjacent, they
   terminate this comment and CSS error recovery then eats the scope root
   while the build stays green (rift.css and aerosol.css both carry the
   telling). Say "the terminator"; never quote it.
   ========================================================================= */


/* -------------------------------------------------------------------------
   1. THE SCOPE ROOT — geometry, the ladder, the two paints, the glass
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] {
  /* geometry: water-worn, one small radius everywhere */
  --r: 6px;
  --r-lg: 8px;
  --r-s: 6px;
  --r-xs: 4px;
  --r-pill: 999px;

  --maxw: 1200px;

  /* the ladder */
  --lr-mast: clamp(34px, 6.4vw, 84px);
  --lr-h1: clamp(30px, 4.6vw, 58px);
  --lr-h2: clamp(22px, 3.0vw, 36px);
  --lr-fig: clamp(30px, 4.4vw, 58px);
  --lr-body: 15px;
  --lr-label: 10.5px;
  --lr-track: .22em;

  /* THE PAINTS, NAMED BY JOB (own-world 4).
     ★★ WIDENED 2026-08-24 on his second ruling — "more colors to match
     background". The palette was three paints against a picture that has
     FOUR families in it. Re-measured off the 20.7% of source-background.png
     that is actually saturated paint:

         230-250deg  blue / indigo ... 34.4%  -> --lr-mark   (accent)
         320-350deg  magenta+crimson . 41.4%  -> --lr-commit / --lr-red
         260-290deg  VIOLET .......... 11.2%  -> --lr-violet   <- WAS MISSING
         330-340deg  rose ............  (in the 41%) -> --lr-rose

     The violet is an eighth of his artwork and the site did not have one drop
     of it. Both new paints are DERIVED with color-mix from paints the owner's
     manifest already declares, deliberately: `theme.palette` has a fixed key
     set, and a raw literal here would break the theme sheets' no-raw-colour
     gate AND smuggle a colour that is not the owner's. Mixing his own blue
     into his own magenta lands on the violet his own wall is painted with. */
  --lr-mark: var(--accent);
  --lr-mark-hi: var(--accent-hi);
  --lr-commit: var(--primary);
  --lr-violet: color-mix(in srgb, var(--accent) 50%, var(--primary));
  --lr-violet-hi: color-mix(in srgb, var(--lr-violet) 62%, var(--fg));
  --lr-rose: color-mix(in srgb, var(--primary) 55%, var(--bad));
  /* the full arc of his wall, left to right, used by every rule that wants
     the WHOLE palette rather than one job: dividers, sweeps, edges, glows */
  --lr-arc: linear-gradient(90deg,
      var(--lr-mark) 0%,
      var(--lr-violet) 34%,
      var(--lr-commit) 62%,
      var(--lr-rose) 82%,
      var(--lr-red) 100%);
  /* THE THIRD INK: the logo's red half. It is NOT a paint with a job (blue
     MARKS, magenta COMMITS); it is the DANGER paint (--bad, set to the crest's
     own left-arc crimson) borrowed as the warm channel of the tear so the
     split reads as the crest's literal red<->blue arc. A 1px shadow, never a
     filled slab, so it does not collide with danger's filled plate. */
  --lr-red: var(--bad);
  --lr-commit-hi: color-mix(in srgb, var(--primary) 82%, var(--fg));
  --lr-slab-ink: var(--bg-deep);
  --lr-wash: color-mix(in srgb, var(--accent) 12%, transparent);
  --lr-wash-2: color-mix(in srgb, var(--lr-commit) 14%, transparent);

  /* THE GLASS (own-world 3): one film, one sink, one up-light. */
  --lr-pane: color-mix(in srgb, var(--bg) 66%, transparent);
  --lr-pane-hi: color-mix(in srgb, var(--bg) 52%, transparent);
  --lr-pane-sink: blur(12px) saturate(112%);
  --lr-uplight: linear-gradient(to top,
      color-mix(in srgb, var(--accent) 7%, transparent),
      transparent 46%);
  --lr-edge: color-mix(in srgb, var(--accent) 22%, transparent);
  --lr-edge-hi: color-mix(in srgb, var(--accent) 58%, transparent);

  /* THE DROPOUT HAIRLINE (own-world 2): the line, and the walking hole. */
  --lr-hair: 1px;
  --lr-hole: 46px;
  --lr-hole-run: 8s;

  /* THE SPLIT SIGNATURE (own-world 5): two 1px shadows, never a stroke.
     Blue up-left, RED down-right — the crest's own red<->blue chromatic arc,
     not the commit paint. */
  --lr-split: -1px -1px 0 color-mix(in srgb, var(--lr-mark-hi) 60%, transparent),
              1px 1px 0 color-mix(in srgb, var(--lr-red) 60%, transparent);
  --lr-split-torn: -3px 0 0 color-mix(in srgb, var(--lr-mark-hi) 80%, transparent),
                   3px 0 0 color-mix(in srgb, var(--lr-red) 80%, transparent);

  /* the wall's veils: the plate is DARK (p1 luminance ~1, p75 at 77), so the
     veil opens WIDE — most of this painting is honest black wall and 15px
     type reads straight over it; only the moonlit band needs damping. */
  --veil: color-mix(in srgb, var(--bg-deep) 46%, transparent);
  --veil-deep: color-mix(in srgb, var(--bg-deep) 72%, transparent);

  /* THE GLITCH (the owner's second ruling, 2026-08-24: "give website glitch
     affects, glitch sounds, smooth transitions, glitch transitions, glitch
     animations for when a page opens and they scroll"). The tear rule above
     was the theme's ONE glitch and it only ever answered a heading; these are
     the tokens the whole vocabulary below is built from, so a future change
     is one edit rather than forty.

     THE SPLIT DISTANCE IS THE ONLY MAGNITUDE. Everything that glitches on
     this site displaces by --lr-gl, in the crest's two paints, on the x axis
     only — one grammar, so a card, a heading, a button and the page itself
     all read as the same fault rather than as four effects. */
  /* ★ TURNED UP on his second ruling — "whole website needs to be glitchy
     with cool animations". The first cut was a polite glitch: 3px, one
     heading at a time, a flick every 26 seconds. These are the numbers the
     whole vocabulary scales from, so the dial is here and nowhere else. */
  --lr-gl: 4px;
  --lr-gl-far: 10px;
  --lr-gl-fast: .34s;
  --lr-gl-slow: .68s;
  /* the two ghosts a glitching element throws: cold up-left, warm down-right */
  --lr-ghost-cold: color-mix(in srgb, var(--lr-mark-hi) 74%, transparent);
  --lr-ghost-warm: color-mix(in srgb, var(--lr-red) 74%, transparent);
  --lr-gl-shadow: calc(-1 * var(--lr-gl)) 0 0 var(--lr-ghost-cold),
                  var(--lr-gl) 0 0 var(--lr-ghost-warm);
  --lr-gl-shadow-far: calc(-1 * var(--lr-gl-far)) 0 0 var(--lr-ghost-cold),
                      var(--lr-gl-far) 0 0 var(--lr-ghost-warm);
  /* the scanline the whole page wears, and the sweep that walks down it */
  --lr-scan: color-mix(in srgb, var(--accent) 7%, transparent);
  --lr-sweep: color-mix(in srgb, var(--lr-violet-hi) 13%, transparent);

  /* re-point the house families at the charged-wall look */
  --surface: var(--lr-pane);
  --surface-2: var(--lr-pane-hi);
  --border: color-mix(in srgb, var(--accent) 16%, transparent);
  --edge: var(--lr-edge);
  --edge-lit: var(--lr-edge-hi);
  --grad-card: linear-gradient(180deg, var(--lr-pane-hi), var(--lr-pane));
  --accent-wash: var(--lr-wash);
  --accent-wash-2: var(--lr-wash-2);
  --accent-glow: color-mix(in srgb, var(--accent) 34%, transparent);
}

/* -------------------------------------------------------------------------
   2. THE KEYFRAMES — the whole motion vocabulary, in one place
   ------------------------------------------------------------------------- */
@keyframes lr-dropout {
  from { mask-position: calc(-1 * var(--lr-hole)) 0; }
  to   { mask-position: calc(100% + var(--lr-hole)) 0; }
}
@keyframes lr-tear-a {
  0%, 100% { transform: translateX(0); opacity: 0; }
  8%  { transform: translateX(-5px); opacity: .9; }
  16% { transform: translateX(3px); opacity: .7; }
  24% { transform: translateX(-2px); opacity: .85; }
  32% { transform: translateX(0); opacity: 0; }
}
@keyframes lr-tear-b {
  0%, 100% { transform: translateX(0); opacity: 0; }
  8%  { transform: translateX(5px); opacity: .9; }
  16% { transform: translateX(-3px); opacity: .7; }
  24% { transform: translateX(2px); opacity: .85; }
  32% { transform: translateX(0); opacity: 0; }
}
@keyframes lr-surface {
  from { transform: translateY(14px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
@keyframes lr-shimmer {
  from { background-position: 0 0; }
  to   { background-position: 0 6px; }
}

/* -------------------------------------------------------------------------
   3. THE WALL — the owner's painting as the charged wall, no ruled ground
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] body {
  font-size: var(--lr-body);
  letter-spacing: .005em;
}

/* the backdrop plate at full strength; backdrop.css supplies the element and
   this sheet re-veils it. ★ THE VEIL ITSELF LIVES IN SECTION 14b — ONE PLACE,
   ON PURPOSE. Three different rules in this file used to paint this same
   pseudo-element, each written in a different round, each at the identical
   specificity, so which one a reader actually saw was decided by file order.
   Two are now comments and 14b is the rule; the numbers there sit next to the
   rendered measurement that chose them. */

/* ★★ THE HOUSE LIFT IS CANCELLED ON THIS OWNER, AND THE REASON IS ARITHMETIC.
   `backdrop.css .wc-plane` carries `filter: brightness(1.34) saturate(1.1)`,
   set for an owner whose plate was a dark montage where "only the skull is
   really visible". THIS plate is the opposite problem: measured p95 = 196,
   p99 = 248 — it is neon paint on true black, already at the top of the
   range. A 1.34x multiply on pixels that start above 190 clips them to white,
   so the chains lose their highlights, the crown loses its drip and the whole
   plate goes chalky. Held at 1.0 (a no-op multiply, so true black stays true
   black exactly as the house sheet promises) with a touch of saturation for
   the print. The selector is raised rather than hammered.
   ★ The plate ITSELF is the fix for "so oversized" — tools/owner_artset/
   widewall.py rebuilt it as a LANDSCAPE poster plate, so `cover` down-scales
   it instead of blowing a portrait picture up 1.7x and showing a third of it. */
html[data-site-theme="lair"] .night .wc-plane {
  filter: brightness(1) saturate(1.08);
}

/* -------------------------------------------------------------------------
   4. THE CHROME — nav as a surfaced instrument; every bracket cancelled
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] .nav {
  background: color-mix(in srgb, var(--bg-deep) 72%, transparent);
  backdrop-filter: var(--lr-pane-sink);
  border-bottom: var(--lr-hair) solid var(--lr-edge);
}

/* ★ A CANCEL MUST NAME EVERY SHAPE THE HOUSE DECLARES (the 08-24 law):
   terminal.css draws the current-page bracket at BOTH
   `.nav .nav-links a[aria-current="page"]::before` (0,3,2) AND
   `.nav .nav-links.grouped a[aria-current="page"]::before` (0,4,2), plus the
   lang-row variant. Every property the house rule sets is reset here, at a
   specificity above each. */
html[data-site-theme="lair"] .nav .nav-links a::before,
html[data-site-theme="lair"] .nav .nav-links a::after,
html[data-site-theme="lair"] .nav .nav-links.grouped a::before,
html[data-site-theme="lair"] .nav .nav-links.grouped a::after,
html[data-site-theme="lair"] .nav .nav-links a[aria-current="page"]::before,
html[data-site-theme="lair"] .nav .nav-links a[aria-current="page"]::after,
html[data-site-theme="lair"] .nav .nav-links.grouped a[aria-current="page"]::before,
html[data-site-theme="lair"] .nav .nav-links.grouped a[aria-current="page"]::after,
html[data-site-theme="lair"] .nav .nav-in:has(.nav-lang:not([hidden]):not(:empty)) .nav-links.grouped a[aria-current="page"]::before {
  content: none;
  margin: 0;
  border: 0;
  background: none;
}

html[data-site-theme="lair"] .nav .nav-links a {
  position: relative;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: var(--lr-track);
  text-transform: uppercase;
  color: var(--fg-dim);
  padding: 9px 11px;
  border-radius: var(--r-xs);
  transition: color .18s ease, background-color .18s ease, text-shadow .18s ease;
}
html[data-site-theme="lair"] .nav .nav-links a:hover {
  color: var(--fg);
  background: var(--lr-wash);
  text-shadow: var(--lr-split);
}
/* the current page is UNDER-LIT: a dropout hairline beneath the word — the
   theme's own state mark, never the house bracket. */
html[data-site-theme="lair"] .nav .nav-links a[aria-current="page"] {
  color: var(--lr-mark-hi);
}
html[data-site-theme="lair"] .nav .nav-links a[aria-current="page"]::after {
  content: "";
  position: absolute;
  left: 11px;
  right: 11px;
  bottom: 3px;
  height: var(--lr-hair);
  background: var(--lr-mark);
  mask: linear-gradient(90deg,
      black 0, black calc(50% - var(--lr-hole) / 2),
      transparent 50%,
      black calc(50% + var(--lr-hole) / 2), black 100%)
      0 0 / calc(100% + 2 * var(--lr-hole)) 100% no-repeat;
  animation: lr-dropout var(--lr-hole-run) linear infinite;
}

/* the brand: the split signature lives on the site's own name.
   ★ AND IT IS SET IN THE DISPLAY FACE AT A READABLE SIZE. The house draws the
   lockup at 12px in whatever `--font-display` is; on Anton — a face drawn for
   72px posters — 12px uppercase with 0.14em tracking rendered as a squashed
   grey smear that the owner named first ("for the fonts and texts color its
   hurrendous"). This owner's display face is now Russo One, which is a WIDE
   face with open counters at small sizes, and the size comes up to 13.5px.
   Weight 400, deliberately: Russo One ships ONE weight, so any `font-weight`
   above 400 is the browser SYNTHESISING a bold — smearing the stems of a face
   that is already heavy. Every rule in this sheet that names the display face
   names weight 400 with it, for that reason. */
html[data-site-theme="lair"] .nav .brand b,
html[data-site-theme="lair"] .nav .brand span b {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: .02em;
  color: var(--fg);
  text-shadow: var(--lr-split);
}
html[data-site-theme="lair"] .nav .brand .brand-txt > span {
  font-family: var(--font-ui);
  color: var(--fg-mute);
  letter-spacing: .18em;
}

/* ★ THE TWO TERMINAL CARETS, CANCELLED — this is the "not terminal" ask
   finishing the job the first cut left half done. The house look draws a
   blinking block cursor in TWO places, and both survived the first lair
   build: `.brand-cursor` (a real element in the lockup markup, which every
   other site theme in this registry cancels by name) and `.hero h1::after`
   (a drawn box hung off the masthead, blinking at ~1 Hz). On the owner's own
   screenshot the second one reads as a blue rectangle floating after the
   island's name — the single most terminal-looking mark on the page, and it
   was on the masthead. `display: none` on the element, and content + every
   drawn property on the pseudo, because clearing `content` alone leaves a
   box that still carries the background. */
html[data-site-theme="lair"] .brand-cursor { display: none; }
html[data-site-theme="lair"] .hero h1::after {
  content: none;
  display: none;
  width: 0;
  height: 0;
  margin: 0;
  background: none;
  animation: none;
}

/* ★ NOT TERMINAL (his ask, verbatim). terminal.css prints a SHELL PROMPT
   before the hero kicker - `.hero .kicker::before { content: var(--prompt) }`
   renders `dinoqueenslair.com:~$` above the masthead, the single most
   terminal-reading thing on the page. It is cancelled here whole (terminal.css
   loads BEFORE this sheet, so a raised selector wins), and the kicker itself
   is re-voiced as a plain blue label with the theme's mono index tick. */
html[data-site-theme="lair"] .hero .kicker::before {
  content: "";
  width: 26px;
  height: var(--lr-hair);
  background: var(--lr-mark);
  border-radius: 0;
  color: transparent;
  flex: 0 0 auto;
}
html[data-site-theme="lair"] .hero .kicker {
  font-family: var(--font-ui);
  font-weight: 700;
  letter-spacing: var(--lr-track);
  color: var(--lr-mark-hi);
  text-transform: uppercase;
}

/* ★★ THE MASTHEAD IS INK, NOT PAINT — and this is the other half of "the
   fonts and texts color its hurrendous".
   `terminal.css:2116` sets `.hero h1 { color: var(--accent-hi) }`, which on
   this owner is the blue arc of the crest. The island's NAME was therefore
   painted in the same blue as every link, every eyebrow and every live
   reading on the page, sitting over the artwork's own blue chain, and it
   measured 4.44:1 there. lair.css named `.sec-head h1` and `.page-h1` and
   never `.hero h1`, so the one heading a visitor reads first was the one
   heading the theme did not own.
   Own-world 4 is explicit that BLUE MARKS and never carries the reading: the
   ink on this site is `--fg` and the paints are carried by the SPLIT. So the
   masthead takes the full ink token, keeps the crest's red<->blue chromatic
   shadow, and steps up to the mast size the ladder already declared. */
html[data-site-theme="lair"] .hero h1 {
  font-size: var(--lr-mast);
  line-height: 1.02;
  color: var(--fg);
  -webkit-text-fill-color: var(--fg);
  text-shadow: var(--lr-split);
}
html[data-site-theme="lair"] .hero .lede {
  color: var(--fg-dim);
  max-width: 46ch;
}

/* ★★ THE TYPEWRITER ENTRANCE IS CANCELLED, FOR TWO REASONS AT ONCE.
   `terminal.css:2302` types the kicker and the masthead in with `tm-type`, a
   clip-path reveal on `steps()` — a teletype drawing the island's name one
   character at a time. That is the house voice this owner rejected by name
   ("not terminal"), and it is also the one entrance on the page that FOUGHT
   the glitch: section 12b hands `.hero h1` the boot fault while `.lr-booting`
   rides, so for 900ms `tm-type` was out-ranked and stood down — and the
   instant the class came off, `tm-type` (delay .5s, fill `backwards`) started
   again FROM ZERO. The island's name glitched in, vanished, and then typed
   itself a second time. Caught on a rendered LIVE screenshot at t=1.4s with
   the masthead reading "Dino Queen's La"; no gate saw it and no static
   screenshot could have.
   Cancelled at (0,3,2), above the house rule's (0,2,2), and ABOVE this line in
   the file so section 12b's boot fault — which ties on specificity and comes
   later — still wins for its one shot. */
html[data-site-theme="lair"][data-wc-motion="on"] .hero .kicker,
html[data-site-theme="lair"][data-wc-motion="on"] .hero h1 {
  animation: none;
  clip-path: none;
}

/* -------------------------------------------------------------------------
   5. THE TEAR RULE — region headings (own-world 1)
   The base heading is finished and solid. The two tear slivers are painted
   by ::before and ::after clones on hover and while `.lr-torn` (the script's
   burst) rides — both die whole under reduced motion.
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] .sec-head { position: relative; }
/* ★ THE EYEBROW TAKES THE LIGHT BLUE, NOT THE DEEP ONE. `--accent` measures
   4.44:1 on this ground; `--accent-hi` measures 7.6:1. At 10.5px with 0.22em
   of tracking the deep blue was legible in a contrast checker and hard work
   on a screen — small caps at wide tracking need the HIGHER end of a hue, and
   the deep blue keeps its job as the paint that FILLS (rules, ticks, the
   current-page underline), where area does the work instead of stroke width.
   The size comes up half a point with it. */
html[data-site-theme="lair"] .sec-head .eyebrow,
html[data-site-theme="lair"] .page-head .eyebrow {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: var(--lr-track);
  text-transform: uppercase;
  color: var(--lr-mark-hi);
}
/* ★ NOT TERMINAL (his ask). The PAGE-HEAD eyebrow carries the shell prompt
   too - `:is(.page-head, .sec-head:has(> .page-h1)) .eyebrow::before {
   content: var(--prompt) }` prints `dinoqueenslair.com:~$` before every page
   title. Cancelled at a raised selector that names the same page-head shape
   the house rule does, so a diamond-tick label replaces the prompt rather
   than a gap. */
html[data-site-theme="lair"] :is(.page-head, .sec-head:has(> .page-h1)) .eyebrow::before,
html[data-site-theme="lair"] .sec-head .eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border: var(--lr-hair) solid var(--lr-mark);
  border-radius: 0;
  background: none;
  transform: rotate(45deg);
  color: transparent;
  letter-spacing: 0;
}
html[data-site-theme="lair"] .page-h1,
html[data-site-theme="lair"] .sec-head h1,
html[data-site-theme="lair"] .sec-head h2 {
  position: relative;
  font-size: var(--lr-h2);
  letter-spacing: .01em;
  color: var(--fg);
  text-shadow: var(--lr-split);
}
html[data-site-theme="lair"] .page-h1,
html[data-site-theme="lair"] .sec-head h1 { font-size: var(--lr-h1); }

/* the tear slivers: top third slips one way in blue, bottom third the other
   way in red. data-lr-text is stamped by lair.js; WITHOUT the script
   the hover fires the same slip on the shadows alone — still a finished,
   styled state, script only sharpens it. */
html[data-site-theme="lair"] .sec-head h1[data-lr-text]::before,
html[data-site-theme="lair"] .sec-head h2[data-lr-text]::before,
html[data-site-theme="lair"] .page-h1[data-lr-text]::before {
  content: attr(data-lr-text);
  position: absolute;
  inset: 0;
  color: var(--lr-mark-hi);
  text-shadow: none;
  clip-path: inset(0 0 62% 0);
  opacity: 0;
  pointer-events: none;
}
html[data-site-theme="lair"] .sec-head h1[data-lr-text]::after,
html[data-site-theme="lair"] .sec-head h2[data-lr-text]::after,
html[data-site-theme="lair"] .page-h1[data-lr-text]::after {
  content: attr(data-lr-text);
  position: absolute;
  inset: 0;
  color: var(--lr-commit);
  text-shadow: none;
  clip-path: inset(66% 0 0 0);
  opacity: 0;
  pointer-events: none;
}
html[data-site-theme="lair"] .sec-head:hover h1[data-lr-text]::before,
html[data-site-theme="lair"] .sec-head:hover h2[data-lr-text]::before,
html[data-site-theme="lair"] .page-h1.lr-torn::before,
html[data-site-theme="lair"] .sec-head .lr-torn::before {
  animation: lr-tear-a .9s steps(12) 1;
}
html[data-site-theme="lair"] .sec-head:hover h1[data-lr-text]::after,
html[data-site-theme="lair"] .sec-head:hover h2[data-lr-text]::after,
html[data-site-theme="lair"] .page-h1.lr-torn::after,
html[data-site-theme="lair"] .sec-head .lr-torn::after {
  animation: lr-tear-b .9s steps(12) 1;
}
/* without the stamped copy, hover still answers: the split widens */
html[data-site-theme="lair"] .sec-head:hover h1:not([data-lr-text]),
html[data-site-theme="lair"] .sec-head:hover h2:not([data-lr-text]) {
  text-shadow: var(--lr-split-torn);
}

/* the section divider under a head: the dropout hairline, full width */
html[data-site-theme="lair"] .sec-head::after {
  content: "";
  display: block;
  margin-top: 12px;
  height: var(--lr-hair);
  background: linear-gradient(90deg, var(--lr-edge-hi), var(--lr-edge) 55%,
      transparent);
  mask: linear-gradient(90deg,
      black 0, black calc(38% - var(--lr-hole) / 2),
      transparent 38%,
      black calc(38% + var(--lr-hole) / 2), black 100%)
      0 0 / calc(100% + 2 * var(--lr-hole)) 100% no-repeat;
  animation: lr-dropout var(--lr-hole-run) linear infinite;
}

/* -------------------------------------------------------------------------
   5b. THE FACE — one weight, and figures stop being telemetry
   -------------------------------------------------------------------------
   ★★ THE HEADING WEIGHT IS REAL NOW, AND THAT IS THE POINT.
   `terminal.css:308` sets every heading shape on the site — h1..h4, .page-h*,
   .sec-head h2, .card h3, .cta-card h2, .feat h3, .gate h3 and six console
   labels — to `font-family: var(--font-display); font-weight: 600`.

   THE FIRST TWO FACES BOTH SHIPPED ONE WEIGHT. Anton (the original) and Russo
   One (the first correction) are both single-weight files, so every one of
   those rules asked the browser to FAKE a semibold by smearing the outline —
   merely soft at 58px, and at the 15px of a `.feat h3` it closes the counters
   and prints the mush the owner photographed twice. The first fix pinned them
   all to 400, which stopped the smearing and left the headings limp.

   The display face is now **Orbitron**, a VARIABLE font carrying the real
   400-900 range, so headings can be genuinely heavy: 700 here, and it is a
   drawn 700, not a synthesised one. UI text moves to **Chakra Petch**, which
   ships real 400/500/600/700 — so the control labels below can ask for 600
   and get a designed 600.

   ★ AND THE FIGURES COME OFF THE MONO FACE. `terminal.css:355` sets `.fig b`
   in `--font-mono`, which is right for a terminal and wrong here twice over:
   "not terminal" is the owner's own word for what this site must not be, and
   own-world 6 already ruled that FIGURES ARE MONUMENTS in the display face.
   The tabular-numerals property stays — a player count that jitters as it
   ticks is a defect in any face. Codes, ids and true tabular data keep the
   mono face; a READING is not data. */
html[data-site-theme="lair"] h1,
html[data-site-theme="lair"] h2,
html[data-site-theme="lair"] h3,
html[data-site-theme="lair"] h4,
html[data-site-theme="lair"] .page-h1,
html[data-site-theme="lair"] .page-h2,
html[data-site-theme="lair"] .page-h3,
html[data-site-theme="lair"] .sec-head h2,
html[data-site-theme="lair"] .hero h1,
html[data-site-theme="lair"] .card h3,
html[data-site-theme="lair"] .cta-card h2,
html[data-site-theme="lair"] .feat h3,
html[data-site-theme="lair"] .gate h3,
html[data-site-theme="lair"] .admin-panel-head,
html[data-site-theme="lair"] .stage-head,
html[data-site-theme="lair"] .cas-card-name,
html[data-site-theme="lair"] .vp-panel-h {
  font-weight: 700;
  letter-spacing: .005em;
}
html[data-site-theme="lair"] .fig b,
html[data-site-theme="lair"] .status .fig b {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--fg);
  text-shadow: var(--lr-split);
}
/* the small caps under a figure: the ui face, never mono */
html[data-site-theme="lair"] .fig span {
  font-family: var(--font-ui);
  letter-spacing: .16em;
}

/* -------------------------------------------------------------------------
   6. THE GLASS — cards, panels, notices, status plates (own-world 3)
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] .card,
html[data-site-theme="lair"] .notice,
html[data-site-theme="lair"] .lva-panel,
html[data-site-theme="lair"] .bn-card,
html[data-site-theme="lair"] #bn-place,
html[data-site-theme="lair"] #bn-status,
html[data-site-theme="lair"] #bd-status,
html[data-site-theme="lair"] .cv-box {
  background: var(--grad-card);
  background-blend-mode: normal;
  backdrop-filter: var(--lr-pane-sink);
  border: var(--lr-hair) solid var(--border);
  border-radius: var(--r);
  position: relative;
  overflow: hidden;
}
html[data-site-theme="lair"] .card::after,
html[data-site-theme="lair"] .bn-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--lr-uplight);
  pointer-events: none;
}
html[data-site-theme="lair"] .card:hover {
  border-color: var(--lr-edge-hi);
  transform: translateY(-2px);
  transition: transform .22s ease, border-color .22s ease;
}

/* figures are monuments (own-world 6) */
html[data-site-theme="lair"] .stat b,
html[data-site-theme="lair"] .bn-pot,
html[data-site-theme="lair"] .lb-count b {
  font-family: var(--font-display), var(--font-ui);
  font-size: var(--lr-fig);
  line-height: 1.05;
  color: var(--lr-mark-hi);
  font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------------------
   7. CONTROLS — magenta commits, blue marks, danger stays the house's
   ------------------------------------------------------------------------- */
/* ★★ THE CONTROL LABEL, RE-CUT. `terminal.css:957` sets every button on the
   site to `--font-display` at `font-weight: 600` and 12.5px uppercase. On
   Anton that was the defect the owner named: a poster face at 12.5px, faux-
   bolded by a browser that had no 600 to load, with 0.1em prised between the
   letters — "HOW TO JOIN" rendered as a row of dark smudges on a magenta
   slab. The display face is now Russo One, which is drawn wide and reads
   cleanly at this size; the fix is to stop SYNTHESISING weight on it (400,
   the one weight it ships) and to open the tracking a little further, which
   is what a wide face wants. Named on the shared control primitives, so the
   whole site's buttons answer to one rule. */
html[data-site-theme="lair"] .btn,
html[data-site-theme="lair"] .lva-btn,
html[data-site-theme="lair"] .gpause-btn,
html[data-site-theme="lair"] .vp-act,
html[data-site-theme="lair"] .cv-go,
html[data-site-theme="lair"] #bn-go {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 12.5px;
  letter-spacing: .15em;
}
html[data-site-theme="lair"] .btn {
  border-radius: var(--r-xs);
  border: var(--lr-hair) solid var(--border);
  background: var(--lr-pane);
  color: var(--fg);
  transition: border-color .16s ease, background-color .16s ease,
              transform .16s ease, text-shadow .16s ease;
}
html[data-site-theme="lair"] .btn:hover {
  border-color: var(--lr-edge-hi);
  transform: translateY(-1px);
}
/* THE COMMITTING SLAB: filled magenta, and the ONLY ink on it is the deep
   ground token — own-world 4's arithmetic, in force. */
html[data-site-theme="lair"] .btn.btn-primary,
html[data-site-theme="lair"] .btn-primary,
html[data-site-theme="lair"] .cv-go,
html[data-site-theme="lair"] #bn-go {
  background: var(--lr-commit);
  border-color: var(--lr-commit);
  color: var(--lr-slab-ink);
  /* a REAL 700 from Chakra Petch, which ships one. The note above is why this
     line exists at all: for two faces running it had to say 400, because a
     synthesised bold closes the counters, and a filled slab is the worst
     place on the site to lose a letter. */
  font-weight: 700;
}
html[data-site-theme="lair"] .btn.btn-primary:hover,
html[data-site-theme="lair"] .btn-primary:hover,
html[data-site-theme="lair"] .cv-go:hover,
html[data-site-theme="lair"] #bn-go:hover {
  background: var(--lr-commit-hi);
  border-color: var(--lr-commit-hi);
}
/* danger is untouched on purpose: the house's filled bad plate + icon + word
   is the fleet's colour-blind-safe grammar and this theme keeps it. */

html[data-site-theme="lair"] input[type="text"],
html[data-site-theme="lair"] input[type="number"],
html[data-site-theme="lair"] .lva-input,
html[data-site-theme="lair"] .bn-field input,
html[data-site-theme="lair"] .cv-n {
  background: color-mix(in srgb, var(--bg-deep) 78%, transparent);
  border: var(--lr-hair) solid var(--border);
  border-radius: var(--r-xs);
  color: var(--fg);
}
html[data-site-theme="lair"] input[type="text"]:focus,
html[data-site-theme="lair"] input[type="number"]:focus,
html[data-site-theme="lair"] .lva-input:focus,
html[data-site-theme="lair"] .bn-field input:focus,
html[data-site-theme="lair"] .cv-n:focus {
  outline: none;
  border-color: var(--lr-mark);
  box-shadow: 0 0 0 3px var(--lr-wash);
}

/* -------------------------------------------------------------------------
   8. THE LIVE BAR — the one scanline on the site
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] #wc-livebar {
  background: color-mix(in srgb, var(--bg-deep) 76%, transparent);
  backdrop-filter: var(--lr-pane-sink);
  border-top: var(--lr-hair) solid var(--lr-edge);
}
html[data-site-theme="lair"] #wc-livebar::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom,
      transparent 0 2px,
      color-mix(in srgb, var(--accent) 4%, transparent) 2px 3px,
      transparent 3px 6px);
  animation: lr-shimmer 1.4s linear infinite;
}

/* -------------------------------------------------------------------------
   9. ENTRANCES — sections surface from below; complete without motion.js
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] .section > .sec-head,
html[data-site-theme="lair"] .grid > * {
  animation: lr-surface .6s ease-out both;
}
html[data-site-theme="lair"] .grid > *:nth-child(2) { animation-delay: .07s; }
html[data-site-theme="lair"] .grid > *:nth-child(3) { animation-delay: .14s; }
html[data-site-theme="lair"] .grid > *:nth-child(4) { animation-delay: .21s; }

/* -------------------------------------------------------------------------
   10. THE FOOTER — glass, and the house's ghost mark cancelled
   ------------------------------------------------------------------------- */
html[data-site-theme="lair"] .foot {
  background: color-mix(in srgb, var(--bg-deep) 66%, transparent);
  backdrop-filter: var(--lr-pane-sink);
  border-top: var(--lr-hair) solid var(--lr-edge);
}
html[data-site-theme="lair"] .foot::before {
  content: none;
}
html[data-site-theme="lair"] .foot-brand img {
  filter: drop-shadow(0 0 10px var(--accent-glow));
}

/* -------------------------------------------------------------------------
   12. THE GLITCH — the fault the whole site speaks in
   -------------------------------------------------------------------------
   THE SECOND RULING (2026-08-24, verbatim): "give website glitch affects,
   glitch sounds, smooth transitions, glitch transitions, glitch animations
   for when a page opens and they scroll, etc. make it very very cool."

   The first cut of this theme had exactly ONE glitch — the tear rule — and it
   only ever answered a region heading. Everything below extends that one idea
   to the whole surface rather than inventing a second effect beside it:

     THE FAULT IS ALWAYS THE SAME FAULT. Every glitching thing on this site
     displaces on the X AXIS ONLY, by --lr-gl, throwing a cold ghost up-left
     and a warm ghost down-right — the crest's own blue<->red arc. A heading
     tearing, a card arriving, a button pressed, the page opening and the page
     LEAVING are five sizes of one event. That is why this reads as a signal
     fault and not as a bag of effects.

     STEPS(), NEVER EASE, FOR THE FAULT ITSELF. A glitch is a sampling
     failure: discrete bad frames, not a smooth slide. Every fault keyframe
     runs on `steps()`; only the ARRIVAL half of an entrance (the settle) is
     eased, because that half is the thing recovering.

     EVERY ANIMATED PROPERTY IS COMPOSITED. transform, opacity, clip-path and
     background-position. No width, no top, no filter animation, no box-shadow
     animation — the page must stay at 60fps on the phone of somebody sitting
     in a queue.

     AND THE PAGE IS FINISHED WITHOUT ANY OF IT. Nothing below is load-bearing
     for legibility: the entrance states are armed by `html.lr-js`, which only
     lair.js can set, so a reader whose script failed gets a complete, static,
     fully-visible page. Under prefers-reduced-motion the whole vocabulary is
     cancelled in section 11 and the same complete page is what is left. */

@keyframes lr-fault {
  0%   { transform: translate3d(0, 0, 0);    text-shadow: var(--lr-split); }
  15%  { transform: translate3d(calc(-1 * var(--lr-gl)), 0, 0);
         text-shadow: var(--lr-gl-shadow); }
  30%  { transform: translate3d(var(--lr-gl), 0, 0);
         text-shadow: var(--lr-gl-shadow-far); }
  45%  { transform: translate3d(calc(-1 * var(--lr-gl)), 0, 0);
         text-shadow: var(--lr-gl-shadow); }
  60%  { transform: translate3d(0, 0, 0);    text-shadow: var(--lr-gl-shadow); }
  100% { transform: translate3d(0, 0, 0);    text-shadow: var(--lr-split); }
}

/* the arrival: three bad frames, then it lands. The clip-path steps are what
   make it read as a picture assembling out of slices rather than a fade. */
@keyframes lr-slice-in {
  0% {
    opacity: 0;
    transform: translate3d(calc(-1 * var(--lr-gl-far)), 14px, 0);
    clip-path: inset(0 0 62% 0);
  }
  22% {
    opacity: 1;
    transform: translate3d(var(--lr-gl-far), 8px, 0);
    clip-path: inset(38% 0 22% 0);
  }
  44% {
    opacity: 1;
    transform: translate3d(calc(-1 * var(--lr-gl)), 4px, 0);
    clip-path: inset(8% 0 0 0);
  }
  66% {
    opacity: 1;
    transform: translate3d(var(--lr-gl), 0, 0);
    clip-path: inset(0 0 0 0);
  }
  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0);
    clip-path: inset(0 0 0 0);
  }
}

/* the page opening: a curtain of the deep ground that tears off in slices */
@keyframes lr-boot-out {
  0%   { opacity: 1; clip-path: inset(0 0 0 0); }
  35%  { opacity: 1; clip-path: inset(0 0 34% 0); }
  55%  { opacity: 1; clip-path: inset(22% 0 34% 0); }
  75%  { opacity: 1; clip-path: inset(22% 0 70% 0); }
  90%  { opacity: 1; clip-path: inset(64% 0 30% 0); }
  100% { opacity: 0; clip-path: inset(100% 0 0 0); }
}
/* the bars that run across that curtain while it is still there */
@keyframes lr-boot-bars {
  from { transform: translate3d(0, -22%, 0); }
  to   { transform: translate3d(0, 22%, 0); }
}
/* the ambient sweep: one soft band walking the page, slowly, forever */
@keyframes lr-sweep {
  from { transform: translate3d(0, -110%, 0); }
  to   { transform: translate3d(0, 110%, 0); }
}
/* the rare whole-page flick the script fires: two bad frames and gone */
@keyframes lr-flick {
  0%, 100% { opacity: 0; transform: translate3d(0, 0, 0); }
  20%      { opacity: 1; transform: translate3d(calc(-1 * var(--lr-gl-far)), 0, 0); }
  40%      { opacity: .7; transform: translate3d(var(--lr-gl-far), 0, 0); }
  60%      { opacity: 1; transform: translate3d(calc(-1 * var(--lr-gl)), 0, 0); }
  80%      { opacity: 0; transform: translate3d(0, 0, 0); }
}

/* ---- 12a. the field: one fixed pane carrying the scanline and the sweep ----
   ★ IT IS ITS OWN ELEMENT AND THAT IS NOT AN ACCIDENT. The house law
   (test_house_look.py::TheGroundIsNeverRuled) is that html/body/main/.night
   and their pseudo-elements carry NO repeating paint, because a rule drawn on
   the ground is a rule the whole site can never escape. `.lr-fx` is a pane
   lair.js appends — a sibling of the content, not the ground under it — which
   is the same seat #wc-livebar's own scanline already occupies. A blocked
   script means no pane and no scanline, which is a correct page. */
html[data-site-theme="lair"] .lr-fx {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  overflow: hidden;
  contain: strict;
}
html[data-site-theme="lair"] .lr-fx-scan {
  position: absolute;
  inset: -10% 0;
  background: repeating-linear-gradient(to bottom,
      transparent 0 2px,
      var(--lr-scan) 2px 3px,
      transparent 3px 5px);
  opacity: .5;
}
html[data-site-theme="lair"] .lr-fx-sweep {
  position: absolute;
  left: 0;
  right: 0;
  height: 26%;
  top: 0;
  background: linear-gradient(to bottom, transparent, var(--lr-sweep),
      transparent);
  animation: lr-sweep 9s linear infinite;
}
/* the flick pane: transparent until the script fires it */
html[data-site-theme="lair"] .lr-fx-flick {
  position: absolute;
  inset: 0;
  opacity: 0;
  background: linear-gradient(92deg,
      color-mix(in srgb, var(--lr-mark-hi) 12%, transparent) 0 18%,
      transparent 34%,
      color-mix(in srgb, var(--lr-red) 10%, transparent) 62% 78%,
      transparent 92%);
}
html[data-site-theme="lair"].lr-flicking .lr-fx-flick {
  animation: lr-flick .3s steps(5) 1;
}

/* ---- 12b. the page opening ----
   The curtain is the DEEP GROUND, so the first frame of a lair page is the
   colour the page settles on rather than a flash of something else. */
html[data-site-theme="lair"] .lr-boot {
  position: fixed;
  inset: 0;
  z-index: 120;
  pointer-events: none;
  background: var(--bg-deep);
  animation: lr-boot-out .62s steps(6) both;
}
html[data-site-theme="lair"] .lr-boot::after {
  content: "";
  position: absolute;
  inset: -25% 0;
  background: repeating-linear-gradient(to bottom,
      transparent 0 6px,
      color-mix(in srgb, var(--accent) 16%, transparent) 6px 8px,
      transparent 8px 15px);
  animation: lr-boot-bars .62s steps(9) both;
}
/* and the masthead itself faults once as the curtain leaves.
   ★★ THE SELECTOR CARRIES [data-wc-motion] AND THAT IS NOT DECORATION.
   Section 16a had to be written at (0,3,2) to out-rank section 4's `tm-type`
   stand-down, and 16a is six hundred lines BELOW this rule. Written the
   plain way this was (0,3,2) as well, lost the tie on file order, and the
   one-shot boot fault silently stopped running — probed on a timeline at
   150/400/700ms, where the masthead reported `lr-idle-split` throughout its
   own entrance. (0,4,2) puts it back above 16a for the 900ms the class
   rides, and the idle split resumes the moment it comes off, which is
   exactly the handover this pair is supposed to have.
   ★ AND THE PAGE MASTHEAD FAULTS IN THE OTHER LANGUAGE. `.page-h1` wears
   the arc as clipped ink now, so `lr-fault` — which animates TEXT-SHADOW —
   would paint its two ghosts straight over that ink for 680ms (16f is the
   whole argument for why that is wrong). Same fault, same distances, cast
   as a filter. */
html[data-site-theme="lair"][data-wc-motion="on"].lr-booting .hero h1 {
  animation: lr-fault var(--lr-gl-slow) steps(9) 1;
}
@keyframes lr-fault-drop {
  0%   { transform: translate3d(0, 0, 0);    filter: var(--lr-split-drop); }
  15%  { transform: translate3d(calc(-1 * var(--lr-gl)), 0, 0);
         filter: var(--lr-gl-drop); }
  30%  { transform: translate3d(var(--lr-gl), 0, 0);
         filter: var(--lr-gl-drop-far); }
  45%  { transform: translate3d(calc(-1 * var(--lr-gl)), 0, 0);
         filter: var(--lr-gl-drop); }
  60%  { transform: translate3d(0, 0, 0);    filter: var(--lr-gl-drop); }
  100% { transform: translate3d(0, 0, 0);    filter: var(--lr-split-drop); }
}
html[data-site-theme="lair"][data-wc-motion="on"].lr-booting .page-h1 {
  animation: lr-fault-drop var(--lr-gl-slow) steps(9) 1;
}

/* ---- 12c. arriving on scroll ----
   `.lr-rise` is stamped by lair.js and the hidden start state is gated on
   `html.lr-js`, so the no-script page never has a section stuck at opacity 0
   — the failure mode the fleet's own eyeball law is written about. */
html[data-site-theme="lair"].lr-js .lr-rise {
  opacity: 0;
  /* ★★ THE STAND-DOWN LIVES HERE, ON THE ARMED ELEMENT ITSELF, AND THE FIRST
     CUT PUT IT SOMEWHERE THAT LOOKED EQUIVALENT AND WAS NOT.
     Section 9's load-time entrance (`lr-surface`, fill `both`) must not run on
     an element the script has armed, or an armed-but-not-yet-scrolled-to head
     would finish that animation at opacity 1 and never have an entrance left
     to play. The obvious cancel was to re-name section 9's own selectors under
     `.lr-js` — `.lr-js .section > .sec-head { animation: none }`. That is
     (0,4,1), which is EXACTLY the arrival rule's specificity, and it sits
     later in the file, so it won the tie and killed the arrival too: every
     `.sec-head` on the site was left at the `.lr-rise` start state, opacity 0,
     permanently invisible. Measured on a real scrolled viewport (three
     stranded heads on the home page alone) — a full-page screenshot reported
     the page as perfect, exactly as the fleet's eyeball law says it would.
     Cancelling on `.lr-rise` instead is (0,3,1): below the arrival rule, which
     is where a cancel belongs. */
  animation: none;
  will-change: transform, opacity, clip-path;
}
html[data-site-theme="lair"].lr-js .lr-rise.lr-in {
  animation: lr-slice-in var(--lr-gl-slow) steps(7) both;
  will-change: auto;
}
/* a grid's children arrive one after another, not as a block */
html[data-site-theme="lair"].lr-js .lr-rise.lr-in:nth-child(2) { animation-delay: .06s; }
html[data-site-theme="lair"].lr-js .lr-rise.lr-in:nth-child(3) { animation-delay: .12s; }
html[data-site-theme="lair"].lr-js .lr-rise.lr-in:nth-child(4) { animation-delay: .18s; }
html[data-site-theme="lair"].lr-js .lr-rise.lr-in:nth-child(5) { animation-delay: .24s; }
html[data-site-theme="lair"].lr-js .lr-rise.lr-in:nth-child(6) { animation-delay: .30s; }
/* and anything the script reached but did not arm keeps the sheet's own
   load entrance — `:not(.lr-rise)` so this can never out-rank the arrival */
html[data-site-theme="lair"].lr-js .section > .sec-head:not(.lr-rise),
html[data-site-theme="lair"].lr-js .grid > *:not(.lr-rise) {
  animation: lr-surface .6s ease-out both;
}

/* ---- 12d. the fault answering a pointer ----
   Hover is the cheapest place a site can feel alive, so every interactive
   surface answers with the same fault at the smallest size. */
html[data-site-theme="lair"] .nav .nav-links a:hover,
html[data-site-theme="lair"] .foot a:hover {
  animation: lr-fault var(--lr-gl-fast) steps(6) 1;
}
html[data-site-theme="lair"] .btn:hover,
html[data-site-theme="lair"] .btn:focus-visible {
  text-shadow: var(--lr-gl-shadow);
}
html[data-site-theme="lair"] .btn:active {
  animation: lr-fault var(--lr-gl-fast) steps(5) 1;
}
/* a card lifts AND faults: the lift is the ease, the ghosts are the steps */
html[data-site-theme="lair"] .card:hover h3,
html[data-site-theme="lair"] .bn-card:hover h3,
html[data-site-theme="lair"] .feat:hover h3 {
  animation: lr-fault var(--lr-gl-fast) steps(6) 1;
}
html[data-site-theme="lair"] .card::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: linear-gradient(96deg,
      color-mix(in srgb, var(--lr-mark-hi) 10%, transparent) 0 12%,
      transparent 30%,
      color-mix(in srgb, var(--lr-commit) 8%, transparent) 68% 84%,
      transparent 96%);
  transition: opacity .22s ease;
}
html[data-site-theme="lair"] .card:hover::before { opacity: 1; }

/* ---- 12e. leaving one page for the next ----
   ★ A CROSS-DOCUMENT VIEW TRANSITION, WHICH IS THE ONLY WAY A MULTI-PAGE SITE
   GETS A SMOOTH ONE. The browsers that do not implement it drop the at-rule
   and every navigation is exactly the instant one it always was — there is no
   fallback to write and nothing to feature-detect. The OLD page leaves on
   steps (it faults out) and the NEW one arrives eased (it recovers), which is
   the same grammar as every entrance above. */
@view-transition { navigation: auto; }
@keyframes lr-vt-out {
  0%   { opacity: 1; transform: translate3d(0, 0, 0); clip-path: inset(0 0 0 0); }
  40%  { opacity: 1; transform: translate3d(calc(-1 * var(--lr-gl-far)), 0, 0);
         clip-path: inset(0 0 26% 0); }
  70%  { opacity: .8; transform: translate3d(var(--lr-gl-far), 0, 0);
         clip-path: inset(30% 0 26% 0); }
  100% { opacity: 0; transform: translate3d(0, 0, 0); clip-path: inset(50% 0 50% 0); }
}
@keyframes lr-vt-in {
  0%   { opacity: 0; transform: translate3d(var(--lr-gl-far), 10px, 0);
         clip-path: inset(46% 0 46% 0); }
  45%  { opacity: 1; transform: translate3d(calc(-1 * var(--lr-gl)), 4px, 0);
         clip-path: inset(12% 0 8% 0); }
  100% { opacity: 1; transform: translate3d(0, 0, 0); clip-path: inset(0 0 0 0); }
}
/* ★ SCOPED ON THE ROOT ELEMENT, not written bare. The view-transition pseudos
   originate on the document element, so `html[data-site-theme="lair"]::view-
   transition-old(root)` is the same pseudo the bare form names — and the
   site-theme gate (test_site_theme.py) requires every selector in a theme
   sheet to carry the scope attribute, for the reason that a bare selector in
   a sheet only one owner loads is still a rule that would apply to any page
   that ever linked it by mistake. */
html[data-site-theme="lair"]::view-transition-old(root) {
  animation: lr-vt-out .3s steps(6) both;
}
html[data-site-theme="lair"]::view-transition-new(root) {
  animation: lr-vt-in .42s cubic-bezier(.2,.8,.2,1) both;
}

/* -------------------------------------------------------------------------
   13. THE WIDENED WALL — his second ruling, in colour and in motion
   -------------------------------------------------------------------------
   "background image isnt covering whole website background ... more colors to
   match background and whole website needs to be glitchy with cool
   animations" (2026-08-24).

   Three separate asks, three separate answers, and only the middle one is a
   colour change:

     COVERAGE is not a stylesheet problem — it was fixed in the PLATE.
     `tools/owner_artset/widewall.py --mode wall` rebuilt the backdrop as
     overlapping panels of his own picture, centre upright and flanks rotated
     180 degrees, alpha-over so the seams vanish. The plate now bleeds to
     every edge with sharp artwork instead of ending in a glow. What this
     sheet then owes it is a scrim that lets type live on a BUSY wall rather
     than a mostly-black one — the veils below are opened up for that, and
     every plate on the page gets a little more sink.

     COLOUR is the token block in section 1: violet and rose derived from his
     own paints, and `--lr-arc`, the whole wall left to right, which every
     rule that used to be one flat blue now uses.

     GLITCH is everything after that: the wall itself faults, not just the
     type on it.
   ------------------------------------------------------------------------- */

/* ---- 13a. reading on a busy wall -------------------------------------- */
/* ★ THE VEIL THAT WAS HERE IS GONE, AND SECTION 14b IS WHY. This block laid a
   62%-to-72% vertical ramp AND a 66% left-to-right ramp over the plate, on
   top of the three scrims backdrop.css already stacks. It was written to let
   type live on a busy wall and it is the single largest reason he could then
   say the picture was "only" in the middle: the only region where all four
   scrims thinned out at once was a band left of centre, half way down. The
   even wash that replaces it lives in 14a/14b so the numbers sit beside the
   measurement that set them. Deleted rather than left dead: a superseded
   rule at the same specificity is one file reorganisation away from winning
   again (see feedback_a_cancel_that_ties_on_specificity...). */
/* glass sinks a little deeper now that there is a picture behind every pane */
html[data-site-theme="lair"] .card,
html[data-site-theme="lair"] .notice,
html[data-site-theme="lair"] .status,
html[data-site-theme="lair"] .lva-panel,
html[data-site-theme="lair"] .bn-card,
html[data-site-theme="lair"] .cv-box {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--bg-deep) 72%, transparent),
      color-mix(in srgb, var(--bg-deep) 84%, transparent));
  backdrop-filter: blur(16px) saturate(118%);
}

/* ---- 13b. THE WALL ITSELF FAULTS -------------------------------------- */
/* backdrop.css already builds `.wc-chroma`: two more copies of the plate,
   offset a couple of pixels and held at 18% — a still chromatic split. On
   this owner it becomes a MOVING one: the offset breathes, so the wall behind
   the page is never quite registered, which is what a bad signal looks like.
   Opacity only creeps between 14% and 26%, so it can never wash the artwork
   out or start strobing. */
@keyframes lr-wall-drift {
  0%, 100% { opacity: .16; transform: translate3d(0, 0, 0); }
  22%      { opacity: .26; transform: translate3d(calc(-1 * var(--lr-gl)), 0, 0); }
  47%      { opacity: .14; transform: translate3d(var(--lr-gl), 0, 0); }
  71%      { opacity: .23; transform: translate3d(calc(-.5 * var(--lr-gl)), 0, 0); }
}
html[data-site-theme="lair"] .night .wc-chroma {
  animation: lr-wall-drift 13s steps(11) infinite;
}
/* and once in a while the whole wall tears sideways for two frames */
@keyframes lr-wall-tear {
  0%, 100% { transform: translate3d(0, 0, 0); }
  30%      { transform: translate3d(calc(-1 * var(--lr-gl-far)), 0, 0); }
  55%      { transform: translate3d(calc(.7 * var(--lr-gl-far)), 0, 0); }
  80%      { transform: translate3d(calc(-.3 * var(--lr-gl-far)), 0, 0); }
}
html[data-site-theme="lair"].lr-flicking .night .wc-plane-base {
  animation: lr-wall-tear .3s steps(5) 1;
}

/* ---- 13c. the arc, wherever a line used to be one flat blue ----------- */
html[data-site-theme="lair"] .sec-head::after {
  background: var(--lr-arc);
}
html[data-site-theme="lair"] .nav {
  border-bottom: 0;
  position: relative;
}
html[data-site-theme="lair"] .nav::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--lr-hair);
  background: var(--lr-arc);
  opacity: .62;
  pointer-events: none;
}
html[data-site-theme="lair"] #wc-livebar {
  border-top: 0;
}
html[data-site-theme="lair"] #wc-livebar::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--lr-hair);
  background: var(--lr-arc);
  opacity: .7;
  pointer-events: none;
}
html[data-site-theme="lair"] .foot {
  border-top: 0;
  position: relative;
}
html[data-site-theme="lair"] .foot::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: var(--lr-hair);
  background: var(--lr-arc);
  opacity: .5;
  pointer-events: none;
}

/* ---- 13d. the third paint gets real work ------------------------------ */
/* A colour with no JOB is decoration, and decoration is what "more colours"
   must not become. Violet is the SECOND-ORDER mark: the things that are
   present but not the subject — a card's lit edge, a focus ring's halo, the
   sweep, an eyebrow's tick. Blue still MARKS and magenta still COMMITS, so
   the two-paint arithmetic in own-world 4 is untouched. */
html[data-site-theme="lair"] .card:hover,
html[data-site-theme="lair"] .bn-card:hover {
  border-color: color-mix(in srgb, var(--lr-violet) 66%, transparent);
}
html[data-site-theme="lair"] .sec-head .eyebrow::before,
html[data-site-theme="lair"] :is(.page-head, .sec-head:has(> .page-h1)) .eyebrow::before {
  border-color: var(--lr-violet-hi);
}
html[data-site-theme="lair"] input[type="text"]:focus,
html[data-site-theme="lair"] input[type="number"]:focus,
html[data-site-theme="lair"] .lva-input:focus,
html[data-site-theme="lair"] .bn-field input:focus,
html[data-site-theme="lair"] .cv-n:focus {
  border-color: var(--lr-violet-hi);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--lr-violet) 26%, transparent);
}
html[data-site-theme="lair"] .foot-brand img {
  filter: drop-shadow(0 0 12px color-mix(in srgb, var(--lr-violet) 46%, transparent));
}

/* ---- 13e. every control answers, and the answer is a fault ------------ */
/* A button sweeps its own arc under the label on hover — the wall's colours
   running through the control, which is the cheapest way to make a page feel
   wired without adding a single new shape. */
html[data-site-theme="lair"] .btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
html[data-site-theme="lair"] .btn::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--lr-arc);
  opacity: 0;
  transform: translate3d(-100%, 0, 0);
  transition: opacity .2s ease, transform .42s cubic-bezier(.2,.8,.2,1);
}
html[data-site-theme="lair"] .btn:hover::after,
html[data-site-theme="lair"] .btn:focus-visible::after {
  opacity: .22;
  transform: translate3d(0, 0, 0);
}
/* the committing slab keeps its magenta and gets the arc as a rim instead,
   because a wash across a filled slab would fight its own ink */
html[data-site-theme="lair"] .btn.btn-primary::after,
html[data-site-theme="lair"] .btn-primary::after {
  inset: auto 0 0 0;
  height: 2px;
  z-index: 0;
  opacity: .9;
  transform: translate3d(-100%, 0, 0);
}
html[data-site-theme="lair"] .btn.btn-primary:hover::after,
html[data-site-theme="lair"] .btn-primary:hover::after {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* ---- 13f. THE HEADINGS ARE NEVER QUITE STILL -------------------------- */
/* The tear rule (own-world 1) fires on hover and on the script's burst. This
   adds the resting state the second ruling asked for: a slow, small,
   permanent misregistration on every region heading, on a 9s cycle that is
   long enough never to read as flicker and short enough that the page is
   visibly alive while somebody reads it. Text-shadow only — no layout, no
   repaint of anything but the glyph's own shadow. */
@keyframes lr-idle-split {
  0%, 86%, 100% { text-shadow: var(--lr-split); }
  88%  { text-shadow: var(--lr-gl-shadow); }
  91%  { text-shadow: var(--lr-split); }
  93%  { text-shadow: var(--lr-gl-shadow-far); }
  96%  { text-shadow: var(--lr-gl-shadow); }
}
html[data-site-theme="lair"] .sec-head h1,
html[data-site-theme="lair"] .sec-head h2,
html[data-site-theme="lair"] .page-h1,
html[data-site-theme="lair"] .hero h1 {
  animation: lr-idle-split 9s steps(1) infinite;
}
/* the boot fault and the arrival still out-rank it: both are later rules on
   an equal-or-higher selector, and both end on the same resting shadow. */

/* ---- 13g. the scramble ------------------------------------------------- */
/* js/lair.js swaps a heading's glyphs for a few frames as it arrives. While
   it does, the heading wears this: the arc as its ink, so the scramble reads
   as the wall coming through the letters rather than as a font error. */
html[data-site-theme="lair"] .lr-scramble {
  background: var(--lr-arc);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  /* ★ A FILL IS NAMED HERE AND IT IS NOT `currentColor`. The aerosol lesson:
     a transparent fill with a currentColor stroke renders INVISIBLE. This is
     transparent ON PURPOSE because a real gradient is clipped behind it, and
     the class only ever rides for ~400ms while the script holds it. If the
     script dies mid-scramble the next rule takes it off. */
  -webkit-text-fill-color: transparent;
}
html[data-site-theme="lair"] .lr-scramble.lr-scramble-done {
  background: none;
  color: var(--fg);
  -webkit-text-fill-color: var(--fg);
}

/* -------------------------------------------------------------------------
   14. THE WALL EVERYWHERE — coverage is a SCRIM problem, not a plate problem
   -------------------------------------------------------------------------
   "for website background right now only middle is using the background,
    have the background cover every tab and whole website background"
   (2026-08-24, his third ruling on this look).

   ★★ THE PLATE WAS ALREADY FULL-BLEED WHEN HE SAID THIS. Section 13 rebuilt
   it as overlapping panels that reach every edge, and it was still true that
   only a middle band of the screen showed any artwork — because FOUR
   independent scrims stack on top of it, and the only place they all thin out
   at once is the middle. Measured on a rendered 1440x900 screen, not guessed:

     .wc-veil        a reading bed:  93% under the nav, a 78% radial at 26/37
     .night::after   section 13a:    62% at the top, 72% at the bottom,
                                     66% down the left, open only past 72%
     main            the page ramp:  40% flat from 648px to the end of the
                                     document, and `main` IS the document
     .wc-page        the column:     58% across the reading column, faded in
                                     by a scroll-driven timeline

   Composited, the reading column below the first screen sat near 75% veil.
   The picture measured p95 [154, 49, 175] on the hero and [52, 28, 57] one
   screen down: the SAME fixed plate, three times darker, because the scrims
   are what changed and not the artwork. That is the whole complaint, and no
   plate could ever have answered it.

   ★★ AND THE CONTRAST BUDGET THOSE NUMBERS WERE DEFENDING WAS NEVER SPENT
   HERE. Rendered-pixel measurement — every glyph hidden, the exact pixels
   under each run of text photographed, the ratio solved against that run's
   own computed colour: 116 runs of text, 9 below AA, and every one of the
   nine failed on a near-BLACK background (p95 luminance .005 to .043). They
   failed on the INK, not on the picture. The scrim was buying nothing at the
   only place it costs the owner his artwork.

   So the scrims open, and the defence moves to where the reading actually
   happens: PLATES. Every text region on this theme sits on translucent glass
   that carries its own ink; the wall shows through it, around it and between
   it, at the same strength on every screen of every page. That is what
   "covers the whole website" means when the site still has to be readable.
   ------------------------------------------------------------------------- */

/* ---- 14a. the four scrims, re-solved ---------------------------------- */
/* Token overrides only — the shapes stay backdrop.css's, so a future change
   there still lands here. Two declarations each, exactly as the house sheet
   does it: the plain token first as the parse-time fallback, the color-mix
   second, so an engine that cannot parse color-mix() gets a blunt scrim
   rather than an invalid variable and a layer that vanishes. */
html[data-site-theme="lair"] {
  /* the reading bed. The radial over the masthead corner was the single
     biggest loss of picture on the first screen. */
  --atmos-read-core: var(--bg-deep);
  --atmos-read-mid:  var(--bg-deep);
  --atmos-read-soft: var(--bg-deep);
  --atmos-corner:    var(--bg-deep);
  --atmos-floor:     var(--bg-deep);
  --atmos-read-core: color-mix(in srgb, var(--bg-deep) 44%, transparent);
  --atmos-read-mid:  color-mix(in srgb, var(--bg-deep) 22%, transparent);
  --atmos-read-soft: color-mix(in srgb, var(--bg-deep) 6%, transparent);
  --atmos-corner:    color-mix(in srgb, var(--bg-deep) 32%, transparent);
  --atmos-floor:     color-mix(in srgb, var(--bg-deep) 84%, transparent);

  /* THE PAGE RAMP IS THE ONE THAT KILLED EVERY SCREEN BELOW THE FIRST.
     It is flat from 72vh to the end of the document, so on a 4000px page it
     is 40% of the owner's picture removed from 3300px of it. At 12% the wall
     is plainly there and the column scrim below still does the reading work. */
  --atmos-page-heavy: var(--bg-deep);
  --atmos-page-light: var(--bg-deep);
  --atmos-page-col:   var(--bg-deep);
  --atmos-page-heavy: color-mix(in srgb, var(--bg-deep) 18%, transparent);
  --atmos-page-light: color-mix(in srgb, var(--bg-deep) 7%, transparent);
  --atmos-page-col:   color-mix(in srgb, var(--bg-deep) 24%, transparent);

  /* THE INK MOVES, because the ink is what actually failed the measurement.
     --fg-mute at 10.5px measured 3.86:1 and 2.65:1 on this owner's OWN near
     black — a house token that has been below AA on every page of this site
     since it was built, and would only get worse with the picture open. Both
     dim tokens are re-mixed out of the owner's own two, so nothing here is a
     colour the manifest does not already declare. */
  --fg-dim:  color-mix(in srgb, var(--fg) 88%, var(--bg));
  --fg-mute: color-mix(in srgb, var(--fg) 72%, var(--bg));

  /* the ink halo. Body copy that lands on a bright chain gets its own dark
     rim, which is legibility the WCAG number cannot see and the reader can.
     One token so it is one edit, and it is dropped wherever a plate already
     does the job. */
  --lr-ink-halo:
     0 1px 2px color-mix(in srgb, var(--bg-deep) 92%, transparent),
     0 0 10px color-mix(in srgb, var(--bg-deep) 74%, transparent);
}

/* ---- 14b. the theme's own veil, evened out ---------------------------- */
/* Section 13a's two ramps are replaced, not added to. What is left is one
   flat wash so the wall reads the same at the top of a screen as at the
   bottom, plus the softest possible close at the very bottom edge where the
   live bar sits. No horizontal ramp at all: a 66% wall down the left was the
   reason the picture looked like it started in the middle of the screen. */
html[data-site-theme="lair"] .night::after {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--bg-deep) 30%, transparent) 0,
      color-mix(in srgb, var(--bg-deep) 12%, transparent) 12%,
      color-mix(in srgb, var(--bg-deep) 12%, transparent) 76%,
      color-mix(in srgb, var(--bg-deep) 46%, transparent) 100%);
}

/* ---- 14c. EVERY TAB, including the one that switched the wall off ----- */
/* "cover every tab" is a per-page claim, so it is checked per page. `.night`
   lives in the shared chrome fragment, so home / live / bounties / vault /
   staff all carry it — and the MAP page was measured at 0x0, because
   `map.css:12` turns it off by name: `.map-page .night { display: none }`,
   on the reasoning that "the basemap IS the backdrop here".

   That reasoning holds while the tiles are there and holds nowhere else. The
   basemap is opaque, so bringing the wall back costs the map nothing where it
   has tiles to draw — and everywhere it does NOT (the water beyond the
   island's edge, a tile that has not loaded, a server whose tiles are not
   installed at all) the reader currently gets a flat slab of floor colour.
   On this owner that slab is one tab of a site whose whole look is a painted
   wall, and it reads as a different website.

   So: the wall comes back UNDER the map, and the map's own ground becomes
   translucent over it rather than opaque. The scrim stays heavy enough
   (58%) that a place name still reads where no tile covers the wall. */
html[data-site-theme="lair"] .map-page .night {
  display: block;
}
html[data-site-theme="lair"] #leafmap,
html[data-site-theme="lair"] .leaflet-container {
  background: color-mix(in srgb, var(--bg-deep) 58%, transparent);
}
/* the rail beside it is glass like every other plate on the site */
html[data-site-theme="lair"] .map-panel {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--bg-deep) 76%, transparent),
      color-mix(in srgb, var(--bg-deep) 86%, transparent));
  backdrop-filter: blur(16px) saturate(118%);
}

/* ---- 14e. the hero copy gets its own ground, not the whole screen ----- */
/* ★ THE SCRIM THAT CAME OFF THE PAGE HAD TO GO SOMEWHERE, AND THIS IS WHERE.
   Opening the four scrims put the picture back on every screen and put the
   picture's brightest object — the blue chain crossing the upper right —
   straight under a 58px masthead. The house answer to that was the reading
   radial in `.wc-veil`, which darkened a quarter of the FIRST SCREEN to
   defend six words.

   This is the same defence at a twentieth of the cost: an elliptical scrim
   that belongs to the copy block instead of to the viewport, anchored where
   the copy actually is, fading to nothing well inside the column. It moves
   with the layout, it is absent on every page that has no hero, and at 390px
   it simply becomes the width of the screen — which is the one width where a
   phone genuinely needs it.

   Radial and not a rectangle, deliberately: a plate behind a headline reads
   as a box, and the one thing this masthead must not look like is a card. */
html[data-site-theme="lair"] .hero-copy {
  position: relative;
  isolation: isolate;
}
html[data-site-theme="lair"] .hero-copy::before {
  content: "";
  position: absolute;
  inset: -14% -26% -18% -34%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(72% 62% at 32% 46%,
      color-mix(in srgb, var(--bg-deep) 78%, transparent) 0%,
      color-mix(in srgb, var(--bg-deep) 58%, transparent) 46%,
      color-mix(in srgb, var(--bg-deep) 22%, transparent) 76%,
      transparent 100%);
}

/* ---- 14f. a heading band gets ground, for the same reason the hero did -- */
/* ★ THIS ONE WAS FOUND BY THE RIG, NOT BY EYE. With the scrims open, the
   measured background under a region heading rose from p95 luminance .006 to
   .08-.15, and the arc ink — which had been solved against the CLOSED wall —
   came back at 1.56:1 to 3.12:1 against the 3:1 large-text floor. Coloured
   type and an open picture cannot both be free; one of them pays.

   Three ways to pay, and only one of them keeps both:
     - close the scrims again ....... gives back the thing he asked for
     - wash the arc toward white .... the masthead experiment, already binned
     - give the HEADING ground ...... 340px of soft ellipse per section

   The band is the cheapest by a wide margin: it is local, it moves with the
   layout, it costs the picture a soft patch behind six words instead of a
   quarter of every screen, and it is the same device 14e uses on the hero,
   so the page has one idea about this and not two. */
html[data-site-theme="lair"] .sec-head,
html[data-site-theme="lair"] .page-head {
  position: relative;
  isolation: isolate;
}
html[data-site-theme="lair"] .sec-head::before,
html[data-site-theme="lair"] .page-head::before {
  content: "";
  position: absolute;
  inset: -28% -22% -18% -12%;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(64% 74% at 26% 50%,
      color-mix(in srgb, var(--bg-deep) 74%, transparent) 0%,
      color-mix(in srgb, var(--bg-deep) 52%, transparent) 44%,
      color-mix(in srgb, var(--bg-deep) 18%, transparent) 74%,
      transparent 100%);
}
/* ★ A PAGE HEAD IS NOT A SECTION HEAD AND THE RIG SAID SO. Same band, same
   shape — and `.page-h1` came back 2.26:1 on three pages while every
   `.sec-head h2` cleared 3:1 on both viewports. Two reasons compound: the
   masthead of an inner page is 58px against a section's 36, so it reaches
   well past the ellipse's dense core; and it sits in the first 200px of the
   document, which is the one band of the wall the top-of-viewport ramp no
   longer closes. Wider, taller and denser, for the one element that needed
   it, rather than deepening the band under every heading on the site. */
html[data-site-theme="lair"] .page-head::before {
  inset: -34% -18% -22% -14%;
  background: radial-gradient(82% 92% at 30% 48%,
      color-mix(in srgb, var(--bg-deep) 86%, transparent) 0%,
      color-mix(in srgb, var(--bg-deep) 74%, transparent) 46%,
      color-mix(in srgb, var(--bg-deep) 34%, transparent) 76%,
      transparent 100%);
}
/* ★ AND THE TEAR SLIVERS MUST NOT INHERIT A GROUND. Own-world 1 draws a
   heading's two clipped slivers with ::before/::after on the H ELEMENT, not
   on `.sec-head`, so the band above cannot collide with them — restated
   because the two live four hundred lines apart and the next person to add a
   pseudo-element here will not remember which of the two boxes is free. */

/* ---- 14d. plates, because the reading moved onto them ----------------- */
/* A feature block was bare text on the wall — the one region on the home
   page with no plate of its own, and the place a bright chain runs straight
   under 15px copy. It becomes glass like everything else, which also finishes
   a grid that read as unfinished floating. */
html[data-site-theme="lair"] .feat {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--bg-deep) 66%, transparent),
      color-mix(in srgb, var(--bg-deep) 80%, transparent));
  backdrop-filter: blur(14px) saturate(116%);
  border: var(--lr-hair) solid var(--lr-edge);
  border-radius: var(--r);
  padding: 16px 18px;
}
html[data-site-theme="lair"] .feat:hover {
  border-color: color-mix(in srgb, var(--lr-violet) 62%, transparent);
}
/* ★ A PLATE COSTS COLUMN WIDTH, AND AT 390px THERE IS NONE TO SPEND. The
   feature grid is two-up on a phone, so 18px of padding either side takes
   36px out of a 168px cell and "Own files, ports, tasks, logs, licence and
   backups." wrapped to seven lines. Measured on a rendered 390 shot, which
   is the only place a wrap like that is visible. */
@media (max-width: 560px) {
  html[data-site-theme="lair"] .feat {
    padding: 12px 12px;
  }
}
/* copy that is still deliberately unplated — a lede under a heading, the
   footer's legal line — wears the halo instead */
html[data-site-theme="lair"] .hero .lede,
html[data-site-theme="lair"] .sec-head .lede,
html[data-site-theme="lair"] .page-head .lede,
html[data-site-theme="lair"] .sec-sub,
html[data-site-theme="lair"] .foot .legal,
html[data-site-theme="lair"] .foot small {
  text-shadow: var(--lr-ink-halo);
}

/* -------------------------------------------------------------------------
   15. THE INK TAKES THE WALL'S COLOURS — "for texts add more colors to them"
   -------------------------------------------------------------------------
   (2026-08-24, his third ruling, same message as the coverage ask.)

   ★★ THE OBVIOUS MOVE IS THE WRONG ONE, AND THE ARITHMETIC SAYS SO BEFORE
   ANY OF IT IS BUILT. `--lr-arc` — his wall left to right, blue to crimson —
   is already the sheet's gradient for dividers and sweeps, and painting
   headings with it is one line. But those are the SATURATED paints, and a
   saturated paint is a poor ink over a picture:

       accent  #3B5BFF  relative luminance .156  ->  1.9:1 on the open wall
       primary #F23CB4                      .254  ->  2.6:1
       bad     #FF2545                      .230  ->  2.4:1

   Large text needs 3:1. Every stop of the wall's own arc FAILS as type on
   the wall the arc was measured off — which is not a paradox, it is what
   "these are the highlights in a dark painting" means.

   ★★ SO THE ARC IS RE-MIXED TOWARD THE INK AND BECOMES A SECOND ONE.
   `--lr-arc-ink` walks the same hues in the same order and every stop is
   pulled toward --fg until it clears 4.5:1 even at body size:

       accent-hi (ice)          .465  ->  4.7:1   <- already an ink token
       violet  + 54% fg         .500  ->  5.0:1
       magenta + 48% fg         .422  ->  4.3:1 large / 4.6:1 measured on plate
       rose    + 48% fg         .399
       crimson + 50% fg         .376  ->  3.9:1

   Two arcs, two jobs, and the job is the difference: **--lr-arc is for
   MARKS** (a divider, a rim, a sweep — a shape, never a glyph) and
   **--lr-arc-ink is for TYPE**. Mixing them up is how a heading ends up
   unreadable on the picture it was supposed to celebrate.

   Everything else in this section is one paint doing one job, which is the
   theme's own-world 4 law and is not relaxed here: blue still MARKS, magenta
   still COMMITS, violet is still the second-order mark, crimson is still
   danger and the warm half of the tear. What changes is that the page now
   USES all of them instead of printing nine tenths of its words in one white.
   ------------------------------------------------------------------------- */

html[data-site-theme="lair"] {
  --lr-arc-ink: linear-gradient(96deg,
      color-mix(in srgb, var(--lr-mark-hi) 40%, var(--fg)) 0%,
      color-mix(in srgb, var(--lr-violet) 34%, var(--fg)) 30%,
      color-mix(in srgb, var(--lr-commit) 40%, var(--fg)) 58%,
      color-mix(in srgb, var(--lr-rose) 40%, var(--fg)) 78%,
      color-mix(in srgb, var(--lr-red) 38%, var(--fg)) 100%);
}

/* ---- 15a. headings wear the wall ------------------------------------- */
/* ★ THE FILL IS NAMED TWICE AND THE ORDER IS LOAD-BEARING. `color` is set
   FIRST and to a real ink, so an engine without background-clip:text — or
   one that drops the declaration for any reason at all — prints a finished
   white heading rather than an empty band. The transparent fill only ever
   appears inside @supports, and only where a gradient is already clipped
   behind it. This is the aerosol lesson (a transparent fill with nothing
   painted behind it renders INVISIBLE and shipped once) written as a
   structure instead of as a warning. */
html[data-site-theme="lair"] .sec-head h2,
html[data-site-theme="lair"] .page-h1,
html[data-site-theme="lair"] .page-h2 {
  color: var(--fg);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  html[data-site-theme="lair"] .sec-head h2,
  html[data-site-theme="lair"] .page-h1,
  html[data-site-theme="lair"] .page-h2 {
    background-image: var(--lr-arc-ink);
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    /* ★ THE GRADIENT IS SIZED TO THE BOX, AND A HEADING'S BOX IS THE WHOLE
       COLUMN. `.sec-head h2` is a block: "Built on its own" is 310px of a
       1152px box, so the arc only ever showed its first 27% and the magenta,
       rose and crimson stops were paint nobody would ever see. Shrinking the
       box to the words makes every heading traverse the whole arc, which is
       the difference between "the headings are blue-ish" and "the wall runs
       through the words". max-width keeps a long heading wrapping exactly
       where it wrapped before. */
    width: -moz-fit-content;
    width: fit-content;
    max-width: 100%;
  }
  /* ★ AND THE MASTHEAD IS DELIBERATELY NOT IN THIS LIST. It was, for one
     render. `--lr-arc-mast` walked the name from white into the crown's
     magenta over 58px of cap height, and on a RENDERED shot the second line
     sat in the middle of the picture's brightest chain and went to mush: a
     pale pink headline on a hot magenta wall, measurably worse than the
     white it replaced and visibly worse than anything else on the page. The
     masthead already carries colour — the permanent blue/red chromatic split
     of own-world 5, which is the crest's own arc and reads AS an edge rather
     than as a fill. More colour on the biggest type on the site is the one
     place this ask had to be answered with a no. */
}
/* ★ AND THE SCRAMBLE HAS TO OUT-RANK BOTH. js/lair.js swaps a heading's
   glyphs for ~360ms and 13g paints that state with the saturated arc. Both
   rules above are (0,2,2) inside @supports; the scramble's own is (0,3,0)
   and later, so it still wins — restated here so a future reorganisation of
   this file cannot quietly change which one a reader sees. */
html[data-site-theme="lair"] .lr-scramble.lr-scramble {
  background-image: var(--lr-arc);
}

/* ---- 15b. the small type, which is most of the page ------------------- */
/* An eyebrow is the region's NAME and marks: it keeps the blue. Its tick is
   second-order and takes the violet (13d). The lede under it is reading and
   stays ink. What changes below is everything that used to be printed in one
   undifferentiated grey. */
html[data-site-theme="lair"] .kicker {
  color: var(--lr-mark-hi);
}
/* a step number is an ORDINAL — it walks the arc, one paint per step, so the
   list reads as a sequence rather than as three identical blue marks */
html[data-site-theme="lair"] .steps li:nth-child(1)::before,
html[data-site-theme="lair"] .steps > *:nth-child(1) > .num {
  color: var(--lr-mark-hi);
}
html[data-site-theme="lair"] .steps li:nth-child(2)::before,
html[data-site-theme="lair"] .steps > *:nth-child(2) > .num {
  color: var(--lr-violet-hi);
}
html[data-site-theme="lair"] .steps li:nth-child(3)::before,
html[data-site-theme="lair"] .steps > *:nth-child(3) > .num {
  color: color-mix(in srgb, var(--lr-commit) 52%, var(--fg));
}
html[data-site-theme="lair"] .steps li:nth-child(4)::before,
html[data-site-theme="lair"] .steps > *:nth-child(4) > .num {
  color: color-mix(in srgb, var(--lr-rose) 52%, var(--fg));
}
/* emphasis inside copy: the second-order mark, because emphasis is exactly
   "present, but not the subject" */
html[data-site-theme="lair"] .card strong,
html[data-site-theme="lair"] .notice strong,
html[data-site-theme="lair"] p strong,
html[data-site-theme="lair"] li strong {
  color: var(--lr-violet-hi);
  font-weight: 600;
}
/* a value that IS data keeps the mono face and takes the rose, so a code, an
   id or a path is visibly a different KIND of thing from the prose round it */
html[data-site-theme="lair"] code,
html[data-site-theme="lair"] kbd,
html[data-site-theme="lair"] .mono {
  color: color-mix(in srgb, var(--lr-rose) 56%, var(--fg));
}
/* a link in running copy marks, and COMMITS on hover — the same two paints
   doing the same two jobs they do on a button */
html[data-site-theme="lair"] .card a:not(.btn),
html[data-site-theme="lair"] .notice a:not(.btn),
html[data-site-theme="lair"] p a:not(.btn),
html[data-site-theme="lair"] li a:not(.btn),
html[data-site-theme="lair"] .foot-links a {
  color: var(--lr-mark-hi);
  text-decoration-color: color-mix(in srgb, var(--lr-mark-hi) 42%, transparent);
  text-underline-offset: 3px;
  transition: color .16s ease, text-decoration-color .16s ease;
}
html[data-site-theme="lair"] .card a:not(.btn):hover,
html[data-site-theme="lair"] .notice a:not(.btn):hover,
html[data-site-theme="lair"] p a:not(.btn):hover,
html[data-site-theme="lair"] li a:not(.btn):hover,
html[data-site-theme="lair"] .foot-links a:hover {
  color: color-mix(in srgb, var(--lr-commit) 52%, var(--fg));
  text-decoration-color: var(--lr-commit);
}
/* the feature icon is second-order: it labels, it does not announce */
html[data-site-theme="lair"] .feat-ico {
  color: var(--lr-violet-hi);
}

/* ---- 15c. the figures are monuments, so they are painted like one ----- */
/* own-world 6 already says a count takes the display face at up to 58px.
   Until now it took it in white. A figure is the one place on the page where
   the arc can run its whole length inside a single element and still be read
   at a glance, and it always sits on a plate, so the ink is defended. */
/* ★ THE VALUE, NOT THE BLOCK. `.fig` is a container: a <b> holding the
   number and a <span> holding its caption. Clipping the gradient to `.fig`
   spans it across BOTH, so the caption gets whatever colour happens to fall
   at 80% of the block's height — a 10.5px label painted in the crimson end
   of the arc. Measured on the rendered page: the block is 121px tall and the
   value occupies its top 43. The gradient belongs to the value alone, where
   it is 43px tall, always on a plate, and reads as one object. */
html[data-site-theme="lair"] .fig b,
html[data-site-theme="lair"] .status .fig b {
  color: var(--fg);
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  html[data-site-theme="lair"] .fig b,
  html[data-site-theme="lair"] .status .fig b {
    background-image: var(--lr-arc-ink);
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}

/* ---- 15d. the one measured AA failure left on the site ---------------- */
/* Rendered-pixel measurement, after the scrims opened: 116 runs of text, 5
   failing, all ONE element — the live bar's source chip in its offline
   state, which prints crimson at 10.5px on the bar's own dark plate and
   measures 3.42:1. Danger on this theme is a FILLED plate with its word on
   it (own-world 4), and that is also the fix: deep ground on the crest's
   crimson measures 5.32:1. The other two states are marks and stay outlined. */
html[data-site-theme="lair"] .lb-src.off,
html[data-site-theme="lair"] .status-src.off {
  background: var(--lr-red);
  border-color: var(--lr-red);
  color: var(--lr-slab-ink);
}
/* and the neutral source chip gets a plate of its own rather than sitting as
   bare mute ink on whatever the wall is doing behind the bar */
html[data-site-theme="lair"] .status-src,
html[data-site-theme="lair"] .lb-src {
  background: color-mix(in srgb, var(--bg-deep) 70%, transparent);
  color: var(--fg-dim);
}

/* ---- 15e. selection, because it is text too --------------------------- */
html[data-site-theme="lair"] ::selection {
  background: var(--lr-commit);
  color: var(--lr-slab-ink);
  text-shadow: none;
}

/* ---- 15f. the footer keeps the wall ----------------------------------- */
/* It was the last full-width band on the page with no picture behind it: 66%
   glass, but sat over `main`'s ramp AND its own margin, so it read as a
   black strip closing the site. Opened, and its legal line moves off the
   mono face — a copyright notice is prose, not data. */
html[data-site-theme="lair"] .foot {
  background: color-mix(in srgb, var(--bg-deep) 54%, transparent);
  backdrop-filter: blur(18px) saturate(114%);
}
html[data-site-theme="lair"] .foot-legal,
html[data-site-theme="lair"] .foot-brand span span {
  font-family: var(--font-ui);
  letter-spacing: .04em;
  color: var(--fg-mute);
}

/* -------------------------------------------------------------------------
   16. MORE FAULT — "glitchy affects ... animations, transitions"
   -------------------------------------------------------------------------
   Section 12 built the grammar and section 13 turned it up. This is the third
   pass and it adds surfaces rather than magnitude: the dial in section 1
   (--lr-gl) is untouched, so nothing already on the page gets louder — the
   things that were still perfectly still start answering.
   ------------------------------------------------------------------------- */

/* ---- 16a. THE MASTHEAD'S RESTING FAULT, WHICH NEVER RAN --------------- */
/* ★★ A MEASURED DEFECT, NOT A NEW FEATURE. 13f gives every heading a slow
   permanent misregistration and names `.hero h1` in its selector list — and
   on the masthead it has never once run. Probed, not read:

       getComputedStyle(document.querySelector('.hero h1')).animationName
         -> "none"   at 300 / 600 / 900 / 1100 / 1600 / 2600 ms

   Section 4's `tm-type` cancel is
   `html[...][data-wc-motion="on"] .hero h1 { animation: none }` at (0,3,2).
   13f's rule is (0,2,2). The cancel out-ranks it outright, so the one
   heading a visitor reads first was the only heading on the site that never
   moved. Restated at (0,3,2) and BELOW the cancel, which is the same lesson
   as [[a cancel that ties on specificity]] seen from the other side: there,
   a stand-down beat the rule it protected; here, a stand-down beat a rule
   written six hundred lines later that had nothing to do with it. */
html[data-site-theme="lair"][data-wc-motion="on"] .hero h1 {
  animation: lr-idle-split 9s steps(1) infinite;
}

/* ---- 16b. the chrome answers ------------------------------------------ */
/* Every nav link is a heading-sized promise on this site and none of them
   faulted. A pointer over one now throws the two ghosts for a beat — the
   same grammar as a heading's tear, at a fifth of the distance, because a
   9px link cannot afford a 4px split. */
@keyframes lr-nav-fault {
  0%   { text-shadow: none; }
  30%  { text-shadow: calc(-.4 * var(--lr-gl)) 0 0 var(--lr-ghost-cold),
                      calc(.4 * var(--lr-gl)) 0 0 var(--lr-ghost-warm); }
  60%  { text-shadow: calc(.25 * var(--lr-gl)) 0 0 var(--lr-ghost-cold),
                      calc(-.25 * var(--lr-gl)) 0 0 var(--lr-ghost-warm); }
  100% { text-shadow: none; }
}
html[data-site-theme="lair"][data-wc-motion="on"] .nav-links a:hover,
html[data-site-theme="lair"][data-wc-motion="on"] .nav-auth-btn:hover {
  animation: lr-nav-fault var(--lr-gl-fast) steps(4) 1;
}
html[data-site-theme="lair"] .nav-links a:hover {
  color: var(--lr-violet-hi);
}

/* ---- 16c. a live number that CHANGED says so -------------------------- */
/* js/lair.js watches the readouts the island feeds and stamps `.lr-jolt` on
   one for a beat when its text actually changes. This is the only glitch on
   the site that carries INFORMATION rather than atmosphere, so it is the one
   that is allowed to touch a figure — and it never rewrites the value, only
   ghosts it (contrast the scramble, which is banned from live readings for
   exactly the reason this is allowed: it would show a number the server
   never sent). */
/* ★★ THE ONLY FAULT ON THE SITE MEASURED IN `em`, AND THE LIVE BAR IS WHY.
   Every other fault here displaces by --lr-gl, a flat 4px (10px far), which
   is right because it lands on headings and figures: 4px of 43px type is an
   edge. The jolt is the one that lands on WHATEVER the island happens to
   feed, and the live bar feeds 10.5px caps at .22em tracking. Caught on a
   rendered LIVE screenshot one minute after the deploy: `0 / 100` printed as
   `0//1100` and `CLEAR SKIES` as `CLEEARSSKIES` — a 10px split on 10.5px
   type is not a chromatic edge, it is a second copy of the word.

   A DISPLACEMENT IS ONLY LEGIBLE RELATIVE TO THE TYPE IT DISPLACES. In `em`
   the same keyframe is 0.95px on the bar's caps and 3.9px on a 43px figure,
   which is the same EFFECT at both sizes instead of the same distance. */
@keyframes lr-jolt {
  0%   { text-shadow: -.10em 0 0 var(--lr-ghost-cold),
                       .10em 0 0 var(--lr-ghost-warm); }
  40%  { text-shadow: -.05em 0 0 var(--lr-ghost-cold),
                       .05em 0 0 var(--lr-ghost-warm); }
  70%  { text-shadow: -.08em 0 0 var(--lr-ghost-cold),
                       .08em 0 0 var(--lr-ghost-warm); }
  100% { text-shadow: none; }
}
html[data-site-theme="lair"][data-wc-motion="on"] .lr-jolt {
  animation: lr-jolt var(--lr-gl-slow) steps(6) 1;
}

/* ---- 16d. the plates arrive faulted ----------------------------------- */
/* A card sliding up is the house entrance. A card that arrives with its two
   ghosts still separating is this theme's. It rides on the SAME .lr-in the
   arrival already uses, so no new class, no new observer, and it ends on the
   finished frame like everything else here. */
@keyframes lr-plate-in {
  0%   { box-shadow: calc(-1 * var(--lr-gl-far)) 0 0 -2px var(--lr-ghost-cold),
                     var(--lr-gl-far) 0 0 -2px var(--lr-ghost-warm); }
  55%  { box-shadow: calc(-.4 * var(--lr-gl-far)) 0 0 -3px var(--lr-ghost-cold),
                     calc(.4 * var(--lr-gl-far)) 0 0 -3px var(--lr-ghost-warm); }
  100% { box-shadow: 0 0 0 -4px transparent, 0 0 0 -4px transparent; }
}
html[data-site-theme="lair"].lr-js .card.lr-in,
html[data-site-theme="lair"].lr-js .feat.lr-in,
html[data-site-theme="lair"].lr-js .notice.lr-in,
html[data-site-theme="lair"].lr-js .status.lr-in {
  animation: lr-slice-in var(--lr-gl-slow) steps(7) both,
             lr-plate-in var(--lr-gl-slow) steps(5) both;
}

/* ---- 16e. the focus ring is part of the vocabulary -------------------- */
html[data-site-theme="lair"] :focus-visible {
  outline: 2px solid var(--lr-violet-hi);
  outline-offset: 2px;
}

/* ---- 16f. THE SPLIT ON CLIPPED INK IS A FILTER, NOT A SHADOW ---------- */
/* ★★ A MEASURED DEFECT AND A PAINTING-ORDER LESSON.

   Section 5's signature is two 1px text-shadows on a solid fill. Section 15
   changed the fill of four of those elements to a gradient clipped to the
   glyphs — and a clipped background is painted in the BACKGROUND layer, which
   is BELOW text-shadow in CSS paint order. So on every gradient heading the
   two shadows stopped being an edge and became a 60%-opaque blue-and-red wash
   laid straight over the ink, one pixel off in each direction: the glyph body
   was ~95% covered by them. That is why `page-h1` came back at 2.26:1 with a
   68%-dense scrim already under it, and why "Live stats" rendered lavender
   when the gradient's first stop is a near-white ice blue.

   `filter: drop-shadow()` is the same picture from outside: it takes the
   element's RENDERED result — clipped gradient included — and casts it, so
   the shadows land where a shadow belongs (around the glyph) and the ink
   stays the ink. Two chained drop-shadows give the two paints.

       text-shadow    -> painted OVER a clipped background   (wrong here)
       drop-shadow    -> painted UNDER the rendered element  (right here)

   The tokens below mirror the shadow tokens exactly, so the two signatures
   are the same distance and the same two paints, and nothing else in the
   sheet has to know which of the two mechanisms an element uses. */
html[data-site-theme="lair"] {
  --lr-split-drop:
      drop-shadow(-1px -1px 0 color-mix(in srgb, var(--lr-mark-hi) 60%, transparent))
      drop-shadow(1px 1px 0 color-mix(in srgb, var(--lr-red) 60%, transparent));
  --lr-gl-drop:
      drop-shadow(calc(-1 * var(--lr-gl)) 0 0 var(--lr-ghost-cold))
      drop-shadow(var(--lr-gl) 0 0 var(--lr-ghost-warm));
  --lr-gl-drop-far:
      drop-shadow(calc(-1 * var(--lr-gl-far)) 0 0 var(--lr-ghost-cold))
      drop-shadow(var(--lr-gl-far) 0 0 var(--lr-ghost-warm));
}
html[data-site-theme="lair"] .sec-head h2,
html[data-site-theme="lair"] .page-h1,
html[data-site-theme="lair"] .page-h2,
html[data-site-theme="lair"] .fig b,
html[data-site-theme="lair"] .status .fig b {
  text-shadow: none;
  filter: var(--lr-split-drop);
}
/* the resting misregistration (13f), restated for the elements whose
   signature is now a filter. Same 9s cycle, same two ghosts, same resting
   frame — an element must not be able to tell which mechanism it is on. */
@keyframes lr-idle-drop {
  0%, 86%, 100% { filter: var(--lr-split-drop); }
  88%  { filter: var(--lr-gl-drop); }
  91%  { filter: var(--lr-split-drop); }
  93%  { filter: var(--lr-gl-drop-far); }
  96%  { filter: var(--lr-gl-drop); }
}
html[data-site-theme="lair"] .sec-head h2,
html[data-site-theme="lair"] .page-h1 {
  animation: lr-idle-drop 9s steps(1) infinite;
}

/* -------------------------------------------------------------------------
   11. REDUCED MOTION — the whole vocabulary stops, the page stays finished
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html[data-site-theme="lair"] .nav .nav-links a[aria-current="page"]::after,
  html[data-site-theme="lair"] .sec-head::after,
  html[data-site-theme="lair"] #wc-livebar::before,
  html[data-site-theme="lair"] .section > .sec-head,
  html[data-site-theme="lair"] .grid > *,
  html[data-site-theme="lair"] .sec-head h1[data-lr-text]::before,
  html[data-site-theme="lair"] .sec-head h1[data-lr-text]::after,
  html[data-site-theme="lair"] .sec-head h2[data-lr-text]::before,
  html[data-site-theme="lair"] .sec-head h2[data-lr-text]::after,
  html[data-site-theme="lair"] .page-h1[data-lr-text]::before,
  html[data-site-theme="lair"] .page-h1[data-lr-text]::after {
    animation: none;
  }
  html[data-site-theme="lair"] .card:hover { transform: none; }
  html[data-site-theme="lair"] .btn:hover { transform: none; }

  /* ★★ AND THE WHOLE GLITCH VOCABULARY, WHICH IS THE POINT OF THIS BLOCK.
     A reader who has asked for reduced motion is often asking about exactly
     this class of effect — a fast, high-contrast, sub-second flicker. So it
     does not get gentler here, it STOPS: every fault, every arrival, the
     boot curtain, the ambient sweep and the flick pane. Each one is written
     so that its STOPPED state is the finished one:
       - the fault's 0% and 100% frames are the resting heading, so cancelling
         it leaves the split signature exactly as section 5 draws it
       - `.lr-rise` is forced back to opacity 1, because an entrance that
         never plays must not leave a section invisible (the fleet law:
         "an entrance a reduced-motion reader never receives is a permanently
         invisible section")
       - the boot curtain is hidden outright rather than left mid-tear
       - the cross-document view transition is stood down to a plain cut */
  html[data-site-theme="lair"] .hero h1,
  html[data-site-theme="lair"] .page-h1,
  html[data-site-theme="lair"] .nav .nav-links a:hover,
  html[data-site-theme="lair"] .foot a:hover,
  html[data-site-theme="lair"] .btn:active,
  html[data-site-theme="lair"] .card:hover h3,
  html[data-site-theme="lair"] .bn-card:hover h3,
  html[data-site-theme="lair"] .feat:hover h3,
  html[data-site-theme="lair"] .lr-fx-sweep,
  html[data-site-theme="lair"] .lr-fx-flick,
  html[data-site-theme="lair"] .lr-boot,
  html[data-site-theme="lair"] .lr-boot::after,
  html[data-site-theme="lair"].lr-js .lr-rise.lr-in {
    animation: none;
  }
  html[data-site-theme="lair"][data-wc-motion="on"].lr-booting .hero h1,
  html[data-site-theme="lair"][data-wc-motion="on"].lr-booting .page-h1 {
    animation: none;
  }
  html[data-site-theme="lair"] .lr-boot { display: none; }
  html[data-site-theme="lair"] .lr-fx-sweep { display: none; }
  html[data-site-theme="lair"] .lr-fx-scan { opacity: .25; }
  html[data-site-theme="lair"].lr-js .lr-rise,
  html[data-site-theme="lair"].lr-js .lr-rise.lr-in {
    opacity: 1;
    transform: none;
    clip-path: none;
    will-change: auto;
  }
  html[data-site-theme="lair"] .btn:hover,
  html[data-site-theme="lair"] .btn:focus-visible {
    text-shadow: none;
  }

  /* section 13's additions. The wall stops drifting and tearing, the resting
     headings stop misregistering, and the button sweep becomes an instant
     state rather than a slide — each one back to the finished frame. */
  html[data-site-theme="lair"] .night .wc-chroma,
  html[data-site-theme="lair"].lr-flicking .night .wc-plane-base,
  html[data-site-theme="lair"] .sec-head h1,
  html[data-site-theme="lair"] .sec-head h2,
  html[data-site-theme="lair"] .page-h1,
  html[data-site-theme="lair"] .hero h1 {
    animation: none;
  }
  html[data-site-theme="lair"] .sec-head h1,
  html[data-site-theme="lair"] .hero h1 {
    text-shadow: var(--lr-split);
  }
  /* 16f's elements carry the signature as a FILTER, so the still frame has
     to be named in that language or they come to rest with no signature at
     all — the same trap as cancelling a text-shadow animation by setting
     `animation: none` and forgetting the resting shadow. */
  html[data-site-theme="lair"] .sec-head h2,
  html[data-site-theme="lair"] .page-h1,
  html[data-site-theme="lair"] .page-h2,
  html[data-site-theme="lair"] .fig b {
    text-shadow: none;
    filter: var(--lr-split-drop);
  }
  html[data-site-theme="lair"] .btn::after {
    transition: opacity .2s ease;
    transform: none;
  }
  html[data-site-theme="lair"] .lr-scramble {
    background: none;
    color: var(--fg);
    -webkit-text-fill-color: var(--fg);
  }
  html[data-site-theme="lair"]::view-transition-old(root),
  html[data-site-theme="lair"]::view-transition-new(root) { animation: none; }

  /* ★★ SECTION 16's ADDITIONS, AND THE SPECIFICITY IS THE WHOLE POINT.
     16a had to be written at (0,3,2) to out-rank section 4's `tm-type`
     stand-down, so a cancel here written the plain way — (0,2,2), the shape
     every other rule in this block uses — would LOSE to it and a reader who
     asked for reduced motion would still get a masthead misregistering
     forever. Each cancel below therefore carries the same attribute its rule
     carries. Counted, not assumed; this file has now shipped this exact
     mistake twice in two directions. */
  html[data-site-theme="lair"][data-wc-motion="on"] .hero h1,
  html[data-site-theme="lair"][data-wc-motion="on"] .nav-links a:hover,
  html[data-site-theme="lair"][data-wc-motion="on"] .nav-auth-btn:hover,
  html[data-site-theme="lair"][data-wc-motion="on"] .lr-jolt {
    animation: none;
    text-shadow: none;
  }
  html[data-site-theme="lair"][data-wc-motion="on"] .hero h1 {
    text-shadow: var(--lr-split);
  }
  html[data-site-theme="lair"].lr-js .card.lr-in,
  html[data-site-theme="lair"].lr-js .feat.lr-in,
  html[data-site-theme="lair"].lr-js .notice.lr-in,
  html[data-site-theme="lair"].lr-js .status.lr-in {
    animation: none;
    box-shadow: none;
  }
  /* the arc stays as INK on a heading — it is colour, not motion, and a
     reader who asked for less movement did not ask for a monochrome site */
}
