/* ═══════════════════════════════════════════════════════════════════════════
   darkroom.css — the design layer.

   tokens.css is the system, app.css is the structure, this file is the design.
   Load order: tokens.css → app.css → darkroom.css → adultswim.css.

   It replaces the PHASE E block that used to live inside app.css. That block
   was a corrective layer written over a direction this one abandons, so it was
   deleted rather than left underneath: four stacked passes disagreeing about
   the same component is how the old file got to 3,600 lines, and adding a
   fifth would have been the same mistake with a new palette.

   ── The idea ─────────────────────────────────────────────────────────────
   The library as a film archive. What that buys, concretely:

     · A rail is a strip of film, so it gets sprocket edges and a plate number.
     · A poster is a frame, so it gets a hairline and registration marks, not
       a rounded card with a shadow.
     · A measurement is a readout, so it is set in mono, tracked, uppercase —
       runtimes, years, ratings, episode codes, counts, timecode.
     · Loading is an unexposed frame, not a shimmer.
     · The one accent is a safelight: it appears where something is live or
       where you are, and almost nowhere else.

   ── The rule that keeps it honest ────────────────────────────────────────
   Every technical mark must carry real data. The plate numbers are CSS
   counters over the real DOM, so a hidden rail does not take a number. The
   status strip reads the real service poll. The timecodes are real positions.
   The moment one of them is decorative, all of them read as costume.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ═══ D0 — THE GRAMMAR ══════════════════════════════════════════════════════
   Three things every other section uses. */

/* The plate counter. Numbering is derived from the document, not written into
   it: a section that is display:none generates no box, so it never increments.
   An empty Continue Watching rail does not silently consume plate 01. */
body{ counter-reset:plate; }
.railhead{ counter-increment:plate; }

/* The mono voice. Everything that is a measurement, in one place, so the rule
   cannot drift component by component the way tracking did in the old file. */
.py, .cw-meta, .npc .meta, .npc .se, .slice .meta, .stat .n, .stat .l,
.time, .np-row, .libcount, .foot-count, .kbd, .kbd b, .pstatus,
.syn-g, .lgen, .crow .ck, .storage .ck, .roadmap .ck, .sname .sstate,
.eyebrow, .cap, .npc .who, .syn-more, .menu-label, .libtab, .foot-h,
.badge, .npc .badge, .mf-strip, .mf-plate, .mf-colophon{
  font-family:var(--font-mono);
  font-variant-numeric:tabular-nums;
  font-weight:var(--fw-regular);
}
/* …and the micro-label treatment those share. One caps rule, not eleven. */
.eyebrow, .cap, .npc .who, .syn-g, .lgen, .crow .ck, .storage .ck,
.roadmap .ck, .sname .sstate, .syn-more, .menu-label, .foot-h{
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm);
  text-transform:uppercase; color:var(--text-muted); margin:0;
}
.eyebrow.neon{ background:none; -webkit-text-fill-color:currentColor; color:var(--safelight-text); }
.syn-g, .lgen, .crow .ck, .storage .ck, .roadmap .ck, .syn-more{ color:var(--lighttable-text); }

/* The sprocket rule. Replaces `1px solid` wherever a divider is structural
   rather than incidental. One 20px tile, rastered once, repeated. */
.mf-sprocket{
  height:14px; background-image:var(--sprocket-tile); background-repeat:repeat-x;
  background-position:0 center; opacity:.62;
}

/* The hung margin column. Above 1100px the plate numbers and timecodes live
   out in the margin and the content starts at the second column; below it
   --margin-col is 0 and everything folds back inline. */
.mf-plate{
  position:absolute; left:calc(var(--margin-col) * -1); top:.34em;
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  color:var(--text-faint); pointer-events:none; white-space:nowrap;
}
@media(max-width:1099px){ .mf-plate{ position:static; display:block; margin-bottom:.45em; } }

/* The content track has to make room for what is hung beside it, or the
   numbers render at a negative offset and fall off the left edge. The hero is
   deliberately left out: it is the one full-bleed element on the page. */
@media(min-width:1100px){
  #homeview{ padding-left:calc(var(--gutter) + var(--margin-col)); }
  #catalogview{ padding-left:var(--margin-col); }
}


/* ═══ D1 — TYPE ═════════════════════════════════════════════════════════════
   Instrument Serif for display, Instrument Sans for everything read, JetBrains
   Mono for everything measured. Three sizes per screen: one display, one body,
   one micro-label. The 12-step scale exists to be picked from, not walked. */

.slice h2, .libtitle, .gh-tagline h1, .gate-title, .railhead .cap,
.together-title, .instrument-title, .ltitle, .lb-title{
  font-family:var(--font-serif);
  font-weight:var(--fw-regular);
  font-style:normal;
  line-height:var(--lh-tight);
  letter-spacing:var(--track-display);
  text-wrap:balance;
}
/* The gradient-clipped headline is gone. `.slice h2` was painting a five-stop
   rainbow through the glyphs and animating background-position on hover —
   paint-only, re-rastering the whole glyph run, and the single most reliable
   way to make a page look like a 2019 template. */
.slice h2{
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  -webkit-text-fill-color:currentColor; color:var(--text-primary);
  filter:none; transition:none;
}
.slice.lead h2{ font-size:var(--head-hero); }
.slice:not(.lead) h2{ font-size:clamp(1.25rem,1.9vw,1.75rem); }
/* The lead title's rule: a hard 2px bar with a safelight lead segment, square,
   no glow, no pill cap. */
.slice.lead h2::after{
  content:""; display:block; width:96px; height:2px; margin-top:.7rem;
  border-radius:0; box-shadow:none;
  background:linear-gradient(90deg,var(--accent-base) 0 26px,var(--border-strong) 26px);
}
.slice-syn, .syn-o, .foot-brand p, .gh-tagline .sub{
  font-family:var(--font-sans); text-wrap:pretty; color:var(--text-secondary);
}

/* Body copy sits at --text-base with a reading line-height; nothing on the
   page should be smaller than --text-3xs, and only micro-labels go there. */
body{ font-size:var(--text-base); }

/* Links */
a{ color:var(--safelight-text); text-underline-offset:3px; text-decoration-thickness:1px; }
a:hover{ color:var(--aurora-green-hover); }


/* ═══ D2 — THE TOPBAR BECOMES EQUIPMENT ═════════════════════════════════════
   It was a floating glass pill with a 20px radius, a gilt inner shadow and a
   backdrop blur that re-sampled on every scroll frame over poster art. It is
   now a flat full-bleed slab pinned to the top of the field with one hairline
   and one safelight thread — the only element on the page that is allowed to
   be pinned, so it is the only one that gets an edge. */
#topwrap{ max-width:none; padding:0; }
header{ padding:0; }
#topbar{
  background:var(--glass-header-bg);
  backdrop-filter:none; -webkit-backdrop-filter:none;
  border:0; border-bottom:1px solid var(--border-default);
  border-radius:0; box-shadow:none;
  padding:var(--sp-3) var(--bleed-inset);
  position:relative;
}
/* the safelight thread — the pinned element's one piece of colour */
#topbar::after{
  content:""; position:absolute; left:0; right:0; bottom:-1px; height:1px;
  background:linear-gradient(90deg,var(--accent-base) 0 var(--bleed-inset),
                             color-mix(in srgb,var(--accent-base) 26%,transparent) var(--bleed-inset),
                             transparent 46%);
  pointer-events:none;
}
.topline{ gap:var(--sp-5); }

/* ── The status strip ─────────────────────────────────────────────────────
   Signal's contribution to this direction, and the element that has to earn
   it: a tally lamp bound to the real service poll, the real title count, and
   the local clock at minute resolution. app.js fills it; if a value is
   unknown the field is omitted rather than faked. */
.mf-strip{
  display:flex; align-items:center; gap:var(--sp-5);
  padding:5px var(--bleed-inset);
  background:var(--color-bg-deepest);
  border-bottom:1px solid var(--border-subtle);
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  text-transform:uppercase; color:var(--text-faint);
  white-space:nowrap; overflow:hidden;
}
.mf-strip .f{ display:flex; align-items:center; gap:.55rem; }
.mf-strip .f b{ font-weight:var(--fw-regular); color:var(--lighttable-text); }
.mf-strip .sp{ margin-left:auto; }
.tally{
  display:inline-block; width:7px; height:7px; flex:none;
  background:var(--tally,#3a4a4a); box-shadow:inset 0 0 0 1px rgba(0,0,0,.55);
}
.tally[data-state="live"]{ --tally:var(--accent-base);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.55),0 0 6px color-mix(in srgb,var(--accent-base) 55%,transparent); }
.tally[data-state="queued"]{ --tally:var(--teal-base); }
.tally[data-state="idle"]{ --tally:#3f524d; }
.tally[data-state="down"]{ --tally:transparent; box-shadow:inset 0 0 0 1px var(--border-strong); }
@media(max-width:760px){ .mf-strip .opt{ display:none; } }

/* Nav: the pill goes, an underscore arrives. */
.navlink{
  border-radius:0; padding:var(--sp-2) var(--sp-3);
  font-family:var(--font-sans); font-weight:var(--fw-medium);
  font-size:var(--text-sm); letter-spacing:0; text-transform:none;
  color:var(--text-secondary);
}
.navlink:hover{ background:none; color:var(--text-primary); }
.navlink::after{
  left:var(--sp-3); right:var(--sp-3); bottom:2px; height:1.5px; border-radius:0;
  background:var(--accent-base);
  transition:transform var(--dur-fast) var(--ease-shutter);
}

/* The wordmark. The brandmark SVG is the old gem; the aperture replaces it.
   The drop-shadow that came with the gem goes too: it is a filter render pass
   on an element that is on screen for the whole session, buying a 1px shadow
   nobody can see against a near-black bar. */
.wordmark{ letter-spacing:var(--track-display); }
.wordmark .bar{ color:var(--safelight-text); font-weight:var(--fw-regular); padding:0 .12em; }
.brandmark{ width:1.05em; height:1.05em; filter:none; }
.brandmark .bm-gem, .brandmark .bm-facet, .brandmark .bm-play{ display:none; }
.brandmark{
  background:var(--accent-base);
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
  mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
}


/* ═══ D3 — THE HERO IS A STRIP OF FILM ══════════════════════════════════════
   The existing hero is already a horizontal rail of frames, which is exactly
   what a contact strip is — so it does not need restructuring, it needs to
   stop pretending to be a row of cards. The slices butt together with a
   hairline between them, the strip gets sprocket edges top and bottom, and
   the per-slice radius, shadow, aurora wash and Ken Burns drift all go. */
.railshell#heroshell{ position:relative; margin-bottom:var(--section); }
.hero{
  /* the frames are inset 14px so the sprocket edges sit in clean gutters
     rather than across the artwork — which is how a strip of film is actually
     laid out, and the only way the bottom edge does not land on the copy */
  gap:1px; padding:14px 0; margin-bottom:0;
  background:var(--border-subtle);
  border-top:1px solid var(--border-default);
  border-bottom:1px solid var(--border-default);
  height:clamp(320px,42vw,540px);
  -webkit-mask-image:none; mask-image:none;
}
/* sprocket edges — the strip's identity, one tile each, painted once */
#heroshell::before, #heroshell::after{
  content:""; position:absolute; left:0; right:0; height:14px; z-index:3;
  pointer-events:none; background-image:var(--sprocket-tile);
  background-repeat:repeat-x; background-position:0 center; opacity:.5;
}
#heroshell::before{ top:0; }
#heroshell::after{ bottom:0; }

.slice{
  border-radius:0; border:0; box-shadow:none;
  background:var(--color-bg-deep);
  will-change:auto;
  transition:none;
}
/* No lift, no glow. A frame in a strip does not rise out of the strip; the
   one you are on gets a safelight edge and its artwork brightens. */
.slice::before{
  background:none; mix-blend-mode:normal;
  background-image:var(--grain-tile); background-repeat:repeat;
  opacity:.5;
}
.slice::after{
  background:linear-gradient(0deg,
    var(--color-bg-deepest) 0%,
    color-mix(in srgb,var(--color-bg-deepest) 94%,transparent) 30%,
    color-mix(in srgb,var(--color-bg-deepest) 55%,transparent) 58%,
    color-mix(in srgb,var(--color-bg-deepest) 14%,transparent) 82%,
    transparent 100%);
}
.slice .bg{ transform:none; }
@media(hover:hover){
  .slice:hover, .slice:focus-visible{ transform:none; box-shadow:none; border:0; }
  .slice:hover .in::before, .slice:focus-visible .in::before{ opacity:1; }
}
/* the safelight edge, drawn on the copy block so it aligns with the type */
.slice .in{ position:relative; padding:clamp(18px,2.2vw,30px); }
.slice .in::before{
  content:""; position:absolute; left:0; top:clamp(18px,2.2vw,30px); bottom:clamp(18px,2.2vw,30px);
  width:2px; background:var(--accent-base); opacity:0;
  transition:opacity var(--dur-fast) var(--ease-standard);
}
.slice.lead .in::before{ opacity:.55; }

/* the eyebrow stops being a pill and becomes a slate line */
.slice .hero-eyebrow{
  background:none; border:0; border-radius:0; padding:0;
  color:var(--safelight-text); font-family:var(--font-mono);
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm);
  display:flex; align-items:center; gap:.6rem; margin:0 0 .55rem;
}
.slice .hero-eyebrow::before{
  content:""; width:18px; height:1px; background:var(--accent-base); flex:none;
}
.slice .meta{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); text-transform:uppercase;
  color:var(--lighttable-text); gap:1.1rem; margin-top:.8rem;
}
.slice .meta .r{ color:var(--aurora-amber-hover); text-shadow:none; font-weight:var(--fw-regular); }
.slice-syn{ margin-top:.85rem; max-width:46ch; -webkit-line-clamp:3; }
/* the action never hides — a control that appears on hover is unreachable on
   touch and invisible to anyone reading the page rather than pointing at it */
.slice .go{ opacity:1; transform:none; transition:none; margin-top:1.1rem; }

/* registration marks on the lead frame */
.slice.lead::before{
  background-image:var(--grain-tile),
    url("/static/brand/registration.svg"), url("/static/brand/registration.svg");
  background-repeat:repeat,no-repeat,no-repeat;
  background-position:0 0, 14px 14px, calc(100% - 14px) 14px;
  background-size:auto, 15px 15px, 15px 15px;
}

/* the rail arrows: square, hairline, no blur */
.railnav{
  border-radius:0; background:var(--color-bg-deepest);
  border:1px solid var(--border-strong);
  backdrop-filter:none; -webkit-backdrop-filter:none; box-shadow:none;
  color:var(--text-secondary);
}
.railnav:hover{ background:var(--color-bg-elevated); border-color:var(--accent-base); color:var(--text-primary); }


/* ═══ D4 — RAIL HEADS BECOME PLATES ═════════════════════════════════════════
   A rail heading now carries three things: its plate number hung in the
   margin, the title in display serif, and a sprocket rule under the whole
   rail. The ✦ that used to sit before every heading is gone — a decoration
   standing in for a hierarchy the type should carry — and the mark it used is
   now the registration crosshair on featured artwork, where a mark means
   something. */
.railhead{
  position:relative; align-items:baseline;
  margin:0 0 var(--heading-gap); padding-bottom:var(--sp-3);
}
.railhead .cap{
  position:relative; font-size:var(--head-rail); color:var(--text-primary);
  text-transform:none; letter-spacing:var(--track-display);
}
.railhead .cap::before{
  content:counter(plate,decimal-leading-zero);
  position:absolute; left:calc(var(--margin-col) * -1); top:.5em;
  width:auto; height:auto; margin:0; vertical-align:baseline;
  background:none; -webkit-mask:none; mask:none; filter:none;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); color:var(--text-faint);
}
@media(max-width:1099px){
  .railhead .cap::before{
    position:static; display:inline-block; margin-right:.8em;
    color:var(--accent-base);
  }
}
/* The rule under the head is the sprocket strip, still wiped in on scroll by
   the existing animation-timeline:view() rule — same signature motion, new
   material. It is bottom:-1px on the padded .railhead so it spans the head,
   not just the words. */
.railhead .cap::after{
  left:0; right:auto; bottom:calc(-1 * var(--sp-3)); height:14px;
  width:min(340px,46vw);
  background:var(--sprocket-tile) repeat-x 0 center;
  opacity:.55;
}
/* the secondary action in a rail head, e.g. "Browse the library →" */
.railhead .btn--sm, .railhead .reqline{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
}


/* ═══ D5 — THE CARD IS A FRAME, NOT A CARD ══════════════════════════════════
   The old .pcard was a translucent slab with a blur, a border, a shadow and a
   caption inside it — a card containing artwork. Here the artwork IS the
   object: the frame carries the hairline, and the caption sits on the page
   underneath it the way a caption sits under a plate. */
.pcard{
  background:none; backdrop-filter:none; -webkit-backdrop-filter:none;
  border:0; box-shadow:none; border-radius:0; contain:layout paint;
  transition:transform var(--dur-base) var(--ease-standard),
             opacity var(--dur-base) var(--ease-standard);
}
@media(hover:hover){
  .pcard:hover{ transform:translateY(-3px); box-shadow:none; border:0; }
  /* the spotlight survives: opacity + transform only, compositor-only, and
     over a near-black field it reads as both darker and less saturated
     without paying for a filter pass */
  .addgrid:has(.pcard:hover) .pcard:not(:hover){ opacity:.42; transform:none; }
}
.pcard:active{ transform:translateY(-1px); }

.poster{
  border-radius:0; background:var(--color-bg-deep);
  border:1px solid var(--border-default);
  box-shadow:none;
  transition:border-color var(--dur-fast) var(--ease-standard);
}
@media(hover:hover){ .pcard:hover .poster{ border-color:var(--accent-base); } }
.poster:focus-visible{ border-color:var(--accent-base); }

/* The unexposed frame. The shimmer is gone: it animated background-position
   (paint-only) five times per poster, firing exactly while the main thread was
   busy decoding the image it stood in for. A frame that has not been exposed
   is flat, and that is both cheaper and more honest. (D17 reuses this pseudo
   to carry the hover registration mark: same element, two jobs, no new
   layer.) */
.poster::before{ background-size:auto; animation:none; }

/* Grain + registration marks, above the artwork. z3, pointer-events already
   none. This is the layer that stops an opaque poster from reading as a
   photograph pasted onto a dark page. */
.poster::after{
  content:""; position:absolute; inset:0; z-index:3; pointer-events:none;
  border-radius:0; box-shadow:none;
  background-image:var(--grain-tile);
  background-repeat:repeat;
  opacity:.62;
  transition:opacity var(--dur-fast) var(--ease-standard);
}
@media(hover:hover){ .pcard:hover .poster::after{ opacity:.3; } }

/* The glint becomes a wipe. The old one animated background-position across a
   card-sized gradient — a full re-raster of a decoded poster every frame.
   This is a hard band of light moved by translate3d on a pre-rastered layer,
   which is also the better read: a hard diagonal wipe is a mechanism. */
.pcard::after{
  background:linear-gradient(100deg,transparent 44%,
    color-mix(in srgb,var(--text-primary) 15%,transparent) 50%,transparent 56%);
  background-size:100% 100%; background-position:0 0;
  width:180%; left:-180%; right:auto; inset:0 auto 0 -180%;
  opacity:0;
}
@media(hover:hover){
  .pcard:hover::after{ opacity:1; animation:mf-wipe .42s var(--ease-shutter) both; }
}
@keyframes mf-wipe{ from{ transform:translate3d(0,0,0); } to{ transform:translate3d(240%,0,0); } }

/* The caption. Title in the reading face, everything measured in mono. */
.pt{
  font-family:var(--font-sans); font-weight:var(--fw-medium);
  --pt-lh:1.18;
  font-size:var(--text-body); letter-spacing:0; text-transform:none;
  color:var(--text-primary); line-height:var(--lh-snug);
  margin-top:.7rem; text-align:left;
  /* Exactly two lines, reserved. A `min-height` floor is not enough here:
     the element is a -webkit-box with a line clamp, so a one-line title still
     collapses to one line's worth of box and drops that card's meta row above
     its neighbours' — the ragged baseline across every rail that the 18 Aug
     pass found and only half-fixed. A fixed height holds the row. */
  line-height:var(--pt-lh);
  height:calc(2 * var(--pt-lh) * 1em);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.py{
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  text-transform:uppercase; color:var(--lighttable-text);
  margin-top:.15rem; text-align:left;
}

/* The synopsis overlay: a hard slate, not a frosted panel. */
.poster .syn{
  background:color-mix(in srgb,var(--color-bg-deepest) 92%,transparent);
  backdrop-filter:none; -webkit-backdrop-filter:none; border-radius:0;
  transition:opacity var(--dur-base) var(--ease-standard);
}
.syn-t{ font-family:var(--font-serif); font-weight:var(--fw-regular); letter-spacing:var(--track-tight); }
.syn-o{ font-size:var(--text-xs); line-height:var(--lh-normal); }

/* The request marker. Every card in the Popular rail carries one, so at chip
   size with a border it was eight outlined buttons competing with eight
   posters — the loudest thing in the section, for information that is
   secondary to the artwork. It is a corner tab now: top-left, no border, on a
   scrim, in the accent. Still permanently visible and still readable, because
   "this one is not in the library" is the whole reason that rail exists and
   hiding it behind hover would strand anyone on a touch screen. */
.pstatus{
  top:0; bottom:auto; left:0; right:auto;
  border:0; border-radius:0; backdrop-filter:none; -webkit-backdrop-filter:none;
  padding:4px 7px 4px 8px;
  font-size:9.5px; letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  background:color-mix(in srgb,var(--color-bg-deepest) 86%,transparent);
  color:var(--text-secondary);
}
.pstatus.req{
  color:var(--safelight-text); border:0;
  box-shadow:inset 0 2px 0 var(--accent-base);
}


/* ═══ D6 — CONTINUE WATCHING ════════════════════════════════════════════════ */
.cwcard{ background:none; border:0; box-shadow:none; border-radius:0; }
.cw-thumb{
  border-radius:0; border:1px solid var(--border-default); background:var(--color-bg-deep);
  transition:border-color var(--dur-fast) var(--ease-standard);
}
@media(hover:hover){ .cwcard:hover .cw-thumb{ border-color:var(--accent-base); } }
.cw-info{ padding:.7rem 0 0; }
.cw-t{
  font-family:var(--font-sans); font-weight:var(--fw-medium); font-size:var(--text-body);
  letter-spacing:0; text-transform:none; color:var(--text-primary);
}
.cw-meta{
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  text-transform:uppercase; color:var(--lighttable-text); margin-top:.2rem;
}
/* The resume bar is the reason this rail exists. Square, 3px, flat safelight —
   no gradient, no glow, no infinite highlight sweep. */
.cw-bar{ height:3px; border-radius:0; background:color-mix(in srgb,var(--text-primary) 14%,transparent); }
.cw-bar i{ background:var(--accent-base); box-shadow:none; border-radius:0; }
.cw-play{
  border-radius:0; background:var(--color-bg-deepest);
  border:1px solid var(--border-strong); color:var(--text-primary);
  backdrop-filter:none; -webkit-backdrop-filter:none;
}


/* ═══ D7 — NOW PLAYING BECOMES A RACK ═══════════════════════════════════════ */
.npc{
  background:var(--color-bg-surface); backdrop-filter:none; -webkit-backdrop-filter:none;
  border:1px solid var(--border-default); border-radius:0;
  box-shadow:none;
  background-image:var(--grain-tile); background-repeat:repeat;
}
.npc .ttl{ font-family:var(--font-sans); font-weight:var(--fw-medium); text-transform:none; letter-spacing:0; }
.npc .who{ color:var(--text-muted); }
.npc .meta, .npc .se{
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  text-transform:uppercase; color:var(--lighttable-text);
}
.npc .badge{
  border-radius:0; border:1px solid var(--border-strong);
  background:none; color:var(--text-muted);
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm); text-transform:uppercase;
}
.npc .bar{ height:3px; border-radius:0; background:color-mix(in srgb,var(--text-primary) 14%,transparent); }
.npc .bar i{ background:var(--accent-base); box-shadow:none; border-radius:0; }

/* the playback state line — WATCHING / PAUSED — is a readout like everything
   else on the card */
.np-state{
  display:inline-flex; align-items:center; gap:.45rem;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-secondary);
}
.npc.paused .np-state{ color:var(--text-muted); }
/* the two rails that are horizontal scrollers but are not .addgrid keep their
   scrollbar chrome hidden the same way the rails do — the bar was the only
   thing that read as "utility" rather than "shelf" */
.cwgrid, .nowgrid{ scrollbar-width:none; }
.cwgrid::-webkit-scrollbar, .nowgrid::-webkit-scrollbar{ height:0; }
.libcard{ transition:transform var(--dur-base) var(--ease-standard); }
.p-group .gl{ letter-spacing:var(--track-caps-sm); }

/* The live indicator becomes a tally lamp: square, and its pulse is a scaled
   ring pseudo rather than an animated box-shadow spread. box-shadow spread
   cannot be composited, so the old one was an endless paint loop on an element
   visible for as long as the homepage is open. */
.live{ color:var(--accent-base); position:relative; }
.live::before{
  width:7px; height:7px; border-radius:0; background:var(--accent-base);
  animation:none; box-shadow:none;
}
.live::after{
  content:""; position:absolute; left:0; top:50%; width:7px; height:7px;
  margin-top:-3.5px; border:1px solid var(--accent-base);
  transform-origin:center; opacity:0;
  animation:mf-tally 2.2s var(--ease-standard) infinite;
}
@keyframes mf-tally{
  0%{ transform:scale(1); opacity:.85 }
  70%{ transform:scale(2.6); opacity:0 }
  100%{ transform:scale(2.6); opacity:0 }
}


/* ═══ D8 — CONTROLS ═════════════════════════════════════════════════════════
   One button system, three tiers, and the primary tier is where the value
   ladder finally shows up on screen: a paper fill with ink type. Nothing else
   on the page is light, so the one action that matters reads instantly
   without needing a colour, a gradient or a glow to shout with. */
/* `.btn:not(.btn--accent)` in the base layer is (0,2,0) and re-pills every
   ordinary button, so matching it here is the only way to beat it without an
   !important. Enumerated by computing border-radius across the live page
   rather than by reading selectors. */
.btn, .lbtn, .btn:not(.btn--accent), .btn.btn--sm:not(.btn--accent){
  border-radius:0; border:1px solid var(--border-strong);
  background:transparent; color:var(--text-secondary);
  font-family:var(--font-sans); font-weight:var(--fw-medium);
  font-size:var(--text-sm); letter-spacing:0;
  padding:.5rem 1rem; transition:color var(--dur-fast) var(--ease-standard),
    border-color var(--dur-fast) var(--ease-standard),
    background-color var(--dur-fast) var(--ease-standard),
    transform var(--dur-instant) var(--ease-standard);
}
.btn:not(.btn--accent){ background:transparent; }
.btn:hover, a.lbtn:not(.lplay-go):hover{
  color:var(--text-primary); border-color:var(--text-muted);
  background:color-mix(in srgb,var(--text-primary) 5%,transparent); transform:none;
}
/* press stamps down rather than shrinking — equipment does not squish */
.btn:active, .lbtn:active{ transform:translateY(1px) scale(1); }
.btn--sm{ padding:.3rem .7rem; border-radius:0; }
/* the ripple was a radial gradient repaint on every press; the press state
   above says the same thing for free */
.btn::after, .lbtn::after{ display:none; }

/* primary — the paper tier */
.btn--accent, button.lbtn{
  background:var(--surface-paper); color:var(--on-paper);
  border:1px solid var(--surface-paper); border-radius:0;
  box-shadow:none; font-weight:var(--fw-semibold);
}
.btn--accent:hover, button.lbtn:hover{
  background:var(--surface-paper-hi); border-color:var(--surface-paper-hi);
  color:var(--on-paper); filter:none; transform:none;
}
.btn--accent:active, button.lbtn:active{ transform:translateY(1px); }
.btn--accent::before{ display:none; }

/* the ⌘K trigger */
.search{
  border-radius:0; background:var(--color-bg-deep);
  border:1px solid var(--border-default); box-shadow:none;
  color:var(--text-muted); font-family:var(--font-sans);
}
.search:hover{ border-color:var(--border-strong); }
.search:active{ transform:none; }
.kbd{ gap:3px; }
.kbd b{
  border-radius:0; background:none; border:1px solid var(--border-strong);
  color:var(--text-muted); font-size:var(--text-3xs); letter-spacing:.06em;
  padding:1px 5px; box-shadow:none;
}

.iconbtn{
  border-radius:0; border:1px solid transparent; background:none;
  color:var(--text-secondary);
}
.iconbtn:hover{ border-color:var(--border-strong); color:var(--text-primary); background:none; }
.iconbtn:active{ transform:translateY(1px) scale(1); }
.avatar{ border-radius:0; }

/* fields */
.field, .search, #gate-user, #gate-pass,
input[type="text"], input[type="password"], input[type="search"], select.field{
  border-radius:0; font-family:var(--font-sans);
}
.field:focus, .searchfield:focus-within{
  border-color:var(--accent-base); box-shadow:none; outline:none;
}
.searchfield{ border-radius:0; }

/* one focus ring, everywhere, and it is allowed to win */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid var(--focus-ring); outline-offset:2px; border-radius:0;
}


/* ═══ D9 — THE CATALOG BECOMES A CONTACT SHEET ══════════════════════════════
   Butted frames on a 1px rule, numbered by the grid itself. The gap IS the
   structure — there is no card, no shadow and no radius, so the eye reads a
   sheet of plates rather than a scatter of tiles. */
.libhead{ align-items:flex-end; }
.libtitle{ font-size:var(--head-page); color:var(--text-primary); }
.libtabs{ gap:0; border-bottom:1px solid var(--border-default); }
.libtab{
  border-radius:0; border:0; border-bottom:2px solid transparent; background:none;
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted); padding:.6rem .9rem;
}
.libtab:hover{ color:var(--text-primary); background:none; }
.libtab.on{ color:var(--text-primary); border-bottom-color:var(--accent-base); background:none; }
.view-toggle{ border-radius:0; border:1px solid var(--border-default); background:none; }
.view-toggle button{ border-radius:0; }
.libcount{
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  text-transform:uppercase; color:var(--lighttable-text);
}
/* The contact-sheet grid — butted cells on a 1px rule — was the right idea for
   flat square plates and the wrong one the moment the plates got a radius and
   a shadow: a rounded object inside a ruled cell reads as two systems
   disagreeing. The gap is the structure now, which is also what every gallery
   does with framed work. Checked against the real catalog, not the harness. */
.libgrid{ gap:22px; background:none; border:0; }
.libgrid .pcard{ background:none; padding:0; }
.libgrid .pcard:hover{ transform:translateY(-3px); background:none; }
.libgrid:has(.pcard:hover) .pcard:not(:hover){ opacity:1; }
.libstatus{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); text-transform:uppercase; color:var(--text-muted);
}


/* The empty states, drawn. On a home server these are seen more often than a
   populated grid — a first-run library, a filter with no matches, a service
   that is not answering — and they were a line of grey text. The illustration
   is aria-hidden and the copy carries the whole message, so nothing is lost to
   a screen reader or when images are off. */
.libempty-ill{
  display:block; width:min(320px,70%); height:auto; margin:0 auto var(--sp-6);
  opacity:.8;
}
.libempty-t{
  display:block; font-family:var(--font-serif); font-size:var(--head-sheet);
  letter-spacing:var(--track-display); color:var(--text-primary);
  text-transform:none;
}
.libempty-s{
  display:block; margin-top:.6rem; font-family:var(--font-sans);
  font-size:var(--text-sm); color:var(--text-secondary); text-transform:none; letter-spacing:0;
}
.libempty-b{ margin-top:var(--sp-6); }
.libstatus{ text-align:center; padding:var(--sp-16) 0; }


/* ═══ D10 — OVERLAYS ════════════════════════════════════════════════════════
   Opaque slabs with a hairline. Every backdrop-filter in the app is off: on
   these surfaces a blur is invisible over an opaque panel and still costs a
   full backdrop re-sample for as long as the surface is on screen. */
.overlay, .cmdk, .notif-panel, .kbd-overlay, .kbd-panel, .ctx-menu, .menu,
.lb, .lbox, .loginbox, .pv-box, .sheet{
  backdrop-filter:none !important; -webkit-backdrop-filter:none !important;
}
.lb, .lbox, .loginbox, .pv-box, .kbd-panel, .ctx-menu, .menu, .cmdk-panel{
  border-radius:0; background:var(--color-bg-surface);
  border:1px solid var(--border-strong); box-shadow:var(--shadow-modal);
  background-image:var(--grain-tile); background-repeat:repeat;
}
.overlay, .kbd-overlay, .notif-backdrop{
  background:color-mix(in srgb,var(--color-bg-deepest) 88%,transparent);
}
.sheet-close, .back-to-top, .kbd-help-btn{
  border-radius:0; backdrop-filter:none; -webkit-backdrop-filter:none;
  background:var(--color-bg-surface); border:1px solid var(--border-strong);
  box-shadow:none;
}
.back-to-top:hover, .kbd-help-btn:hover{ transform:none; border-color:var(--accent-base); color:var(--text-primary); }
.menu-item, .ctx-item{ border-radius:0; }
.chip, .badge{ border-radius:0; }
/* ── the last round edges, enumerated ─────────────────────────────────────
   Found by grepping the base layer for `border-radius:50%` and `--r-pill`
   rather than by looking at screens, because a pill three clicks deep in a
   player overlay is exactly the one that survives a visual pass. In an
   archive nothing is round: not the lamps (a panel lamp is square), not the
   chips, not the player controls. The avatar is the single exception, because
   a face is round. */
.pstatus .dot, .sname .dot, .live::before, .chip .sd{ border-radius:0; }
.sheet-close, .pv-btn.btn, .hmenu .iconbtn, .kbd-help-btn, .back-to-top,
.chip, .reqchip, .tp-chip, .pv-fs, .np-join, .lb-poster,
.libtab, .view-toggle button, .npc .badge, .syn-more{ border-radius:0; }
.avatar{ border-radius:50%; }
/* the player's own chrome — scrub bar, knob, the cmdk esc key */
.sbar, .sbar > i, .pv-knob, .notif-badge, .pv-seek, .pv-vol,
.cmdk .esc, .cmdk-esc, span.esc{ border-radius:0; }
.notif-badge{
  border-radius:0; background:var(--accent-base); color:var(--accent-on);
  font-family:var(--font-mono); font-size:var(--text-3xs);
}


/* ═══ D10b — TITLE DETAIL ═══════════════════════════════════════════════════
   The surface where people actually decide what to watch. The structure was
   already right (full-bleed backdrop header, scrolling body, episodes, "more
   like this"); what it needed was this theme's contrast discipline — a scrim
   that actually lands, mono for every measurement, and the one primary action
   in the paper tier so it is not competing with three link badges. */
.lbox{ border-radius:0; }
/* The header art is a decorative surface for type to sit on, not the subject
   of the screen — and when there is no backdrop on file it is the poster blown
   up and blurred, which arrives far brighter than any real backdrop would.
   Dimmed at the source rather than out-gradiented; see D15 for why that is
   done with opacity and not brightness(). */
.lb-hero::after{
  background:
    linear-gradient(0deg, var(--color-bg-surface) 0%,
      color-mix(in srgb,var(--color-bg-surface) 94%,transparent) 24%,
      color-mix(in srgb,var(--color-bg-surface) 62%,transparent) 56%,
      color-mix(in srgb,var(--color-bg-surface) 34%,transparent) 88%),
    linear-gradient(90deg, color-mix(in srgb,var(--color-bg-surface) 82%,transparent),
      color-mix(in srgb,var(--color-bg-surface) 20%,transparent) 58%, transparent 78%);
}
.lb-poster{
  border-radius:0; border:1px solid var(--border-strong); box-shadow:none;
}
.ltitle{
  font-weight:var(--fw-regular); letter-spacing:var(--track-display);
  text-shadow:none; color:var(--text-primary);
}
.lb-kicker{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase; color:var(--safelight-text);
}
.lmeta{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); text-transform:uppercase; color:var(--text-muted);
}
/* A cast list is prose. Setting it in tracked mono capitals made the one piece
   of copy people actually read on this screen the hardest thing on it — which
   is the cost of applying a voice rule by element name rather than by what the
   text is. The KEY stays mono (it is a label); the names are read. */
.lcredits{
  font-family:var(--font-sans); font-size:var(--text-sm); letter-spacing:0;
  text-transform:none; color:var(--text-secondary); line-height:var(--lh-normal);
}
.lcredits b{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted); font-weight:var(--fw-regular);
}
.lgen{
  font-family:var(--font-sans); font-size:var(--text-sm); letter-spacing:0;
  text-transform:none; color:var(--lighttable-text);
}
.lsyn{ font-family:var(--font-sans); color:var(--text-secondary); }
.rbadge{
  border-radius:0; border:1px solid var(--border-default);
  font-family:var(--font-mono); font-weight:var(--fw-regular);
  font-size:var(--text-xs); letter-spacing:var(--track-data);
  color:var(--text-secondary);
}
.rbadge .star{ color:var(--aurora-amber-hover); }
.rbadge .rl{ color:var(--text-faint); letter-spacing:var(--track-caps-sm); font-weight:var(--fw-regular); }
/* section heads inside the body get the same plate treatment as the page's
   rails, minus the number — they are sections of a record, not of the page */
.lb-caphead{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:var(--text-xl); letter-spacing:var(--track-tight);
  text-transform:none;
  color:var(--text-primary); position:relative; padding-bottom:var(--sp-3);
}
.lb-caphead::after{
  content:""; position:absolute; left:0; bottom:0; width:min(220px,40%); height:1px;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--accent-base) 70%,transparent) 0 36px,
    var(--border-default) 36px, transparent 82%);
  opacity:1;
}
/* the modal's Play joins the paper tier; the link badges stay quiet */
.lplay-go{
  background:var(--surface-paper); color:var(--on-paper);
  border:1px solid var(--surface-paper); border-radius:0;
  box-shadow:none; font-weight:var(--fw-semibold);
}
.lplay-go:hover{
  background:var(--surface-paper-hi); border-color:var(--surface-paper-hi);
  color:var(--on-paper); filter:none; transform:none; box-shadow:none;
}
.lplay-go::before, .hero-play::before, .btn--accent::before, .lbtn::before{ display:none; }
.llinks .lbtn{ background:transparent; color:var(--text-secondary); border:1px solid var(--border-default); font-weight:var(--fw-regular); }
.llinks .lbtn:hover{ background:color-mix(in srgb,var(--text-primary) 5%,transparent); color:var(--text-primary); }

/* ── remaining round edges ────────────────────────────────────────────────
   Everything below computed to a pill or a circle in a theme whose radius
   tokens top out at 3px. A pill beside a square frame is the first thing the
   eye catches. The status LAMP stays square too — this theme's lamp is a
   panel lamp; the only round thing left is the avatar, because a face is
   round. */
.themerow button{ border-radius:0; }
.themerow button:hover{ transform:none; outline:1px solid var(--border-strong); outline-offset:2px; }
.themerow button[aria-pressed="true"]{ transform:none; }
.themerow button[aria-pressed="true"]::after{ border-radius:0; inset:-3px; }
.pref-switch-track{ border-radius:0; background:var(--border-strong); }
.pref-switch-track::after{ border-radius:0; background:var(--text-primary); box-shadow:none; }
.pref-switch input:checked ~ .pref-switch-track{ background:var(--accent-base); }
.avatar{ border-radius:0; }
.sheet{ border-radius:0; }
.reqcard, .reqcard-go{ border-radius:0; }


/* ═══ D11 — THE FOOTER LANDS THE PAGE ═══════════════════════════════════════ */
.flourish{ gap:0; }
.flourish i{
  width:100%; max-width:340px; height:14px; background:var(--sprocket-tile) repeat-x 0 center;
  -webkit-mask:none; mask:none; filter:none; opacity:.5; border-radius:0;
}
/* the section mark keeps the mask but loses its glow, for the same reason */
.railhead .cap::before{ filter:none; }
.flourish::before, .flourish::after{
  background:linear-gradient(90deg,transparent,var(--border-default));
}
.flourish::after{ background:linear-gradient(90deg,var(--border-default),transparent); }

footer{ border-top:1px solid var(--border-default); }
.foot-h{ color:var(--text-muted); }
.foot-wordmark{ letter-spacing:var(--track-display); }
.foot-tag{ color:var(--text-secondary); }
.foot-whisper{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); text-transform:uppercase; color:var(--text-faint);
}
.foot-whisper b{ color:var(--lighttable-text); font-weight:var(--fw-regular); }
/* Footer links are standalone navigation, not links inline in a sentence, so
   the inline exception to WCAG 2.5.8 does not cover them — they were 22px
   tall. Padding rather than font-size: the type is right, the target was not.
   (The one genuinely inline link, `id.mediaforest.org` in the gate's closing
   sentence, is exempt and stays as set.) */
footer a{ color:var(--text-secondary); text-decoration:none; display:inline-block; padding:5px 0; }
footer a:hover{ color:var(--text-primary); }
.foot-col a{ min-height:24px; }
/* the Popular rail's request link is a <button>, so it is not exempt either */
.reqline{ padding:6px 0; min-height:24px; display:inline-flex; align-items:center; }

/* The colophon. Naming the faces and the build is the cheapest signal that a
   page was made by someone rather than assembled, and every value in it is
   either static fact or filled from the same telemetry the footer already
   shows. app.js writes #mf-build; if it has nothing, the field stays empty
   rather than inventing a number. */
.mf-colophon{
  display:flex; flex-wrap:wrap; gap:.5rem 1.6rem;
  padding:var(--sp-5) 0 0; margin-top:var(--sp-5);
  border-top:1px solid var(--border-subtle);
  font-size:var(--text-3xs); letter-spacing:var(--track-data);
  text-transform:uppercase; color:var(--text-muted);
}
.mf-colophon b{ color:var(--lighttable-text); font-weight:var(--fw-regular); }


/* ═══ D12 — THE GATE ════════════════════════════════════════════════════════
   The screen every visitor sees, and the one the study found doing the least
   work: two floating rounded boxes centred in a void, the left one holding a
   grey radial smudge and an 18-triangle skyline that rendered as black lumps.

   Rebuilt whole as the reference implementation of this direction. Full bleed,
   type-led, no card. The form is three hairline-underlined fields with no
   boxes and one paper-filled action; the only image on the screen is the mark.
   Everything the old hero drew — three blurred orbs, two rotating rings, a
   pulsing centre dot and the skyline — is deleted, along with five infinite
   animations and three blur filters running on the first paint of every
   visit. */
#gate{
  display:none; padding:0; height:100dvh; overflow:hidden;
  background:var(--color-bg-deepest);
  backdrop-filter:none; -webkit-backdrop-filter:none;
  align-items:stretch; justify-content:stretch;
}
body.locked #gate{ display:grid; grid-template-columns:1fr; }
@media(min-width:900px){ body.locked #gate{ grid-template-columns:1.25fr .85fr; } }

/* ── the display half ── */
.gate-hero{
  position:relative; overflow:hidden; min-height:0;
  display:flex; flex-direction:column; justify-content:center;
  /* app.css centres the hero's children on both axes. In a column flex box
     align-items:center centres them HORIZONTALLY, which silently indented the
     whole copy block away from the padding edge. */
  align-items:flex-start;
  padding:clamp(2rem,6vw,5.5rem);
  background:
    radial-gradient(ellipse 70% 55% at 88% -8%,
      color-mix(in srgb,var(--accent-base) 22%,transparent), transparent 64%),
    var(--color-bg-deepest);
}
.gate-hero::before{
  content:""; position:absolute; inset:0; pointer-events:none;
  background-image:var(--grain-tile); background-repeat:repeat;
}
/* the mark, large and quiet — the only image on the screen */
.gh-mark{
  position:absolute; right:-8%; bottom:-18%; width:min(52%,460px); height:auto;
  opacity:.07; pointer-events:none;
}
.gh-tagline{ position:relative; z-index:3; text-align:left; padding:0; max-width:none; flex:none; }
.gh-tagline h1{ max-width:min(100%,13em); }
.gh-tagline .sub{ max-width:38ch; }
.gh-tagline h1{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  /* Not --head-display. That token tops out at 7rem, which is right for a
     page title with the full width to itself; here the headline shares the
     screen with the form and has ~15ch of measure, and at 112px it wrapped to
     six lines and clipped. Sized for this composition instead. */
  font-size:clamp(2.2rem,4.4vw,4rem); line-height:.98;
  letter-spacing:var(--track-display); margin:0;
  /* not `balance`: it is inherited from the display group and, past four or
     five lines, Chromium balances by making every line short — which is how a
     30-character headline came out one word per line. */
  text-wrap:pretty;
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  -webkit-text-fill-color:currentColor; color:var(--text-primary);
  animation:none;
}
.gh-tagline h1 em{
  font-style:italic; color:var(--safelight-text);
  background:none; -webkit-text-fill-color:currentColor;
}
.gh-tagline .sub{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted); margin:1.5rem 0 0; max-width:none;
}
.gh-plate{
  position:relative; z-index:3; flex:none; margin:1.4rem 0 0;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); text-transform:uppercase; color:var(--text-faint);
}
.gh-rule{
  position:relative; z-index:3; flex:none; height:14px; width:min(320px,80%); margin:1.9rem 0 0;
  background:var(--sprocket-tile) repeat-x 0 center; opacity:.7;
}

/* ── the form half ── */
.gate-card{
  width:auto; text-align:left; gap:0;
  padding:clamp(2rem,5vw,4rem);
  display:flex; flex-direction:column; justify-content:center;
  background:var(--color-bg-deep);
  border:0; border-left:1px solid var(--border-default); border-radius:0;
  backdrop-filter:none; -webkit-backdrop-filter:none; box-shadow:none;
}
@media(max-width:899px){ .gate-card{ border-left:0; border-top:1px solid var(--border-default); } }
.gate-card .wordmark{
  background:none; -webkit-text-fill-color:currentColor; color:var(--text-primary);
  display:flex; align-items:center; justify-content:flex-start; gap:.5rem;
}
.gate-card .wordmark--lg{ font-size:var(--text-md); opacity:1; margin:0 0 2.4rem; }
.gate-title{
  font-size:var(--head-sheet); color:var(--text-primary); margin:0;
  letter-spacing:var(--track-display);
}
.gate-sub{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted); margin:.7rem 0 2rem;
}
.gate-card .loginform{ gap:0; margin:0; }
/* hairline-underlined fields — no boxes. The label is the placeholder, the
   rule is the field, and focus moves the rule to the safelight. */
#gateform .field{
  background:none; border:0; border-bottom:1px solid var(--border-strong);
  border-radius:0; color:var(--text-primary);
  padding:.85rem 0; margin-bottom:1.6rem; width:100%;
  font-size:var(--text-base); transition:border-color var(--dur-fast) var(--ease-standard);
}
#gateform .field::placeholder{ color:var(--text-muted); }
#gateform .field:focus,
#gate-user:focus, #gate-pass:focus{
  border-color:var(--accent-base); box-shadow:none; outline:none;
}
.gate-card .lbtn{
  width:100%; padding:.95rem 1rem; font-size:var(--text-sm);
  border-radius:0; margin-top:.4rem;
}
.gate-status{
  min-height:1.2em; margin:1rem 0 0;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); text-transform:uppercase; color:var(--status-danger);
}
.gate-foot{
  margin:2.6rem 0 0; padding-top:0;
  font-size:var(--text-xs); color:var(--text-muted); line-height:var(--lh-normal);
}
.gate-foot a{ color:var(--lighttable-text); }
@media(max-width:899px){
  body.locked #gate{ overflow-y:auto; }
  .gate-hero{ min-height:46vh; }
  .gate-foot{ margin-top:2rem; }
}


/* ═══ D13 — MOTION, AS ARCHITECTURE ═════════════════════════════════════════
   Three system behaviours instead of forty component animations.

     1. THE STAGGER — a rail's frames arrive in sequence.
     2. THE DEVELOP — artwork emerges rather than appearing.
     3. THE SHUTTER — a view change is a mechanism, not a fade.

   All three are transform/opacity only, none needs a scroll listener, and all
   three collapse under prefers-reduced-motion. */

/* 1 — the stagger. One-shot on first paint, 55ms apart, capped at eight so a
   full catalog grid does not spend four seconds arriving. */
/* the stagger's timings live in D16, where the measurements that set them
   are written down */

/* 2 — the develop. The artwork emerges rather than cutting in. Defined in
   D15, where the reason it is opacity + scale rather than a filter is
   written down. */

/* 3 — the shutter. app.js wraps the catalog open/close in a View Transition
   where the API exists; these are the frames it plays. Two blades meeting in
   the middle — the same gesture as the film gate the theme is named for.
   Where the API is absent nothing here applies and the view simply swaps. */
::view-transition-old(root){ animation:mf-shutter-out 220ms var(--ease-shutter) both; }
::view-transition-new(root){ animation:mf-shutter-in  260ms var(--ease-shutter) both; }
@keyframes mf-shutter-out{
  from{ clip-path:inset(0 0 0 0) } to{ clip-path:inset(50% 0 50% 0) }
}
@keyframes mf-shutter-in{
  from{ clip-path:inset(50% 0 50% 0) } to{ clip-path:inset(0 0 0 0) }
}

/* The scroll hairline. Kept — it is scroll-driven and free — but it is a
   safelight thread now rather than a glowing three-stop gradient. */
body::after{
  height:2px; background:var(--accent-base); box-shadow:none;
}
/* The theme-switch bloom is gone. It painted a full-viewport radial wash at
   z-index 9999 on every palette change; with two themes left it is a 550ms
   flash over the whole page to move between them. */
html::after{ display:none; }


/* ═══ D15 — THE IDLE-PAGE SWEEP ═════════════════════════════════════════════
   Everything below came out of enumerating what a *populated, untouched*
   homepage was still doing: getAnimations() on a page nobody is looking at,
   and every element holding a filter or a compositor layer at rest. This is
   the same method the 17 Aug pass used, applied to what this rewrite left
   behind and to what the base layer still carries.

   The rule for this theme: at rest, the page paints once. The only thing
   allowed to keep moving is the live tally, because a live indicator that
   does not move is not indicating anything. */

/* ── infinite animations, killed ──────────────────────────────────────────
   Four were still running on an idle page. Each is paint-only, and all four
   are decoration standing in for information. */

/* the wordmark's travelling gradient: a five-stop gradient clipped to the
   glyphs and animated on background-position, forever, on an element that is
   on screen for the entire session. It is also why the wordmark measured
   1.13:1 against the topbar — a clipped fill computes as transparent, so
   every contrast check on it was reading the background against itself. */
.sheen{
  background:none; background-size:auto;
  -webkit-background-clip:border-box; background-clip:border-box;
  -webkit-text-fill-color:currentColor;
  color:var(--text-primary); animation:none;
}
/* The divider bar was clipped to a gradient too — and --neon is a solid colour
   now, so it was clipping a solid to text for no reason at all while computing
   `color:transparent`. Anything that computes to a transparent fill is also
   invisible under forced-colors and unmeasurable by a contrast check. */
/* The divider is a glyph, so it is measured like any other text — and the
   graphic-cut accent lands at 4.29:1, just under AA. This is the exact case
   `--safelight-text` exists for: the same hue, cut for type. */
h1 .bar, .wordmark .bar{
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  color:var(--safelight-text); -webkit-text-fill-color:var(--safelight-text); opacity:1;
}
.gate-card .wordmark{ -webkit-text-fill-color:currentColor; }

/* Ken Burns on the lead hero frame. Compositor-only, so it was not expensive —
   but a contact strip is a strip of stills, and a frame that drifts while you
   are reading its title is the one thing on the page arguing with the idea. */
.slice.lead .bg{ animation:none !important; }

/* the now-playing progress bar's highlight sweep: a gradient translated across
   the fill, forever, on every session card */
.npc .bar i::after{ display:none; }

/* the "now playing" dot pulsed by animating box-shadow spread, which cannot be
   composited — an endless paint loop on an element visible whenever the
   homepage is open. Same expanding pulse, rebuilt on transform + opacity in
   D7, so this one just stops. */
.now .cap .live{
  border-radius:0; background:var(--accent-base); box-shadow:none; animation:none;
  width:7px; height:7px;
}

/* ── filters at rest, removed ─────────────────────────────────────────────
   A filter is a separate render pass per element that holds one. Two rules
   here were putting one on every hero backdrop and every poster image on the
   page — roughly 34 live filter passes on an idle homepage.

   The develop entrance is the subtle one: its `to{filter:none}` does NOT
   compute to `none`. Interpolating from `brightness(.4) contrast(1.25)` gives
   the identity `brightness(1) contrast(1)` at 100%, and with fill:both that
   value is retained forever — so a one-shot entrance left a permanent filter
   on every image it touched. Rebuilt on opacity and scale, which are
   compositor-only and cannot leave a residue. */
/* likewise the develop entrance — retimed in D16 */
/* the hero frames sit back until you are on one — done with opacity rather
   than brightness()/saturate(), so eight backdrops cost zero filter passes */
.slice .bg{ filter:none; opacity:.72; transition:opacity var(--dur-slow) var(--ease-standard); }
@media(hover:hover){
  .slice:hover .bg, .slice:focus-visible .bg{ filter:none; opacity:1; }
}
.slice.lead .bg{ opacity:.85; }
/* the detail header art: same reasoning. The blurred stand-in keeps its blur —
   that one is the point, it is one element, and it only exists while the modal
   is open — but it loses the extra brightness pass. */
.lb-art{ filter:none; opacity:.55; }
.lb-art--poster{ filter:blur(22px) saturate(.95); opacity:.4; }

/* ── layers at rest ───────────────────────────────────────────────────────
   `will-change:transform` on .npc promoted one compositor layer per session
   card, permanently, for a hover lift. Scope it to the hover the way the rail
   cards already are, so an idle page holds no speculative layers. */
.npc{ will-change:auto; }
.nowgrid:hover .npc, .nowgrid:focus-within .npc{ will-change:transform; }
.npc:hover{ box-shadow:var(--shadow-md); border-color:var(--border-strong); transform:var(--card-lift-sm); }

/* ── the strip's own contrast ─────────────────────────────────────────────
   --text-faint measured 4.12:1 here. It is the right *tone* for the strip but
   the wrong value for text this small, and the strip is the one place on the
   page where every field is information. */
.mf-strip{ color:var(--text-muted); }
.mf-strip .f b{ color:var(--lighttable-text); }


/* ═══ D16 — THE BOOT STATE, AND MAKING THE THING FEEL FAST ══════════════════
   Measured, not guessed. Frame times on this build are already clean — 16.7ms
   median through a full-page scroll and through a pointer sweep across a rail,
   zero dropped frames. So "slow" was never rendering cost. A filmstrip of the
   first 2.5 seconds showed what it actually was:

     0 – 560ms   the SIGN-IN SCREEN, to a member who is signed in
     560ms       the app, essentially complete

   body ships `.locked` so a signed-out visitor never flashes media. Correct —
   but it meant every signed-in load opened on a full-bleed sign-in page for
   more than half a second. The old gate was a small centred card and got away
   with it; this one does not.

   `booting` is the third state: neither the gate nor the media. It holds a
   frame with the aperture opening — which is finally what that asset is for —
   and it is the only screen a returning member sees before their library. */
#boot{
  position:fixed; inset:0; z-index:1250; display:none;
  align-items:center; justify-content:center;
  background:var(--color-bg-deepest);
}
body.booting #boot{ display:flex; }
/* the gate does not exist in the booting state, and neither does the media */
body.booting #gate{ display:none !important; }
#boot img{ width:56px; height:56px; opacity:.9; }
@media(prefers-reduced-motion:reduce){ #boot img{ opacity:.55; } }

/* ── Motion, retimed ──────────────────────────────────────────────────────
   The second half of "slow" is that the page assembled itself in front of you
   on every visit: a section-level rise, then a per-card stagger running up to
   385ms of delay on top of a 320ms animation, then a scroll-driven reveal that
   held a whole section at opacity 0 until you scrolled to it, then a 480ms
   fade on every image. None of it dropped a frame and all of it read as lag.

   Entrance motion should confirm that something arrived, not perform its
   arrival. Everything below is roughly a third of what it was. */

/* the section-level rise is gone entirely — it was staging the same content
   the card stagger already stages, one layer up */
.rise, .rise.d1, .rise.d2, .rise.d3{ animation:none !important; opacity:1 !important; transform:none !important; }

/* the scroll-driven reveal held Recently Added and Popular at opacity 0 until
   they entered — so the two rails a member scrolls to most were, by design,
   never there when they arrived */
.discover.reveal{ animation:none !important; opacity:1 !important; transform:none !important; }

@media(prefers-reduced-motion:no-preference){
  .addgrid > .pcard, .addgrid > .cwcard{ animation:mf-arrive 190ms var(--ease-enter) both; }
  .addgrid > :nth-child(1){ animation-delay:0ms }
  .addgrid > :nth-child(2){ animation-delay:26ms }
  .addgrid > :nth-child(3){ animation-delay:52ms }
  .addgrid > :nth-child(4){ animation-delay:78ms }
  .addgrid > :nth-child(5){ animation-delay:104ms }
  .addgrid > :nth-child(n+6){ animation-delay:130ms }
  /* the catalog grid gets no stagger at all: it is a hundred cards and an
     infinite scroller, and staging them is a slideshow, not an entrance */
  .libgrid > .pcard{ animation:none; }
}
@keyframes mf-arrive{ from{ opacity:0; transform:translateY(6px) } to{ opacity:1; transform:none } }

/* images arrive, they do not dissolve */
@media(prefers-reduced-motion:no-preference){
  .poster img, .cw-thumb img, .slice .bg{ animation:mf-develop 220ms var(--ease-standard) both; }
}
@keyframes mf-develop{ from{ opacity:0 } to{ opacity:1 } }

/* Hover feedback under ~120ms reads as instant; at 200ms it reads as the page
   thinking about it. This is the single change that most affects whether an
   interface feels responsive, and it costs nothing. */
:root{ --dur-fast:110ms; --dur-base:170ms; }
.pcard{ transition:transform var(--dur-fast) var(--ease-standard), opacity var(--dur-fast) var(--ease-standard); }
.poster, .cw-thumb{ transition:border-color 90ms var(--ease-standard); }
.btn, .lbtn, .navlink, .iconbtn{ transition-duration:100ms; }
.addgrid:has(.pcard:hover) .pcard:not(:hover){ opacity:.55; }

/* Global smooth scrolling makes every jump — skip link, catalog open, back to
   top — a journey. The three places that genuinely want it already pass
   `behavior:'smooth'` explicitly and honour reduced-motion themselves. */
html{ scroll-behavior:auto; }


/* ═══ D17 — GIVING IT A PULSE ═══════════════════════════════════════════════
   The first cut of this theme was correct and inert. It measured well — one
   hue fixed, contrast clean, no filters, no blur, no idle animation — and it
   was boring, because "premium is subtractive" is only half true. Subtraction
   gets you out of a hole. It does not get you anywhere.

   What was missing, specifically:

     - the artwork, the one genuinely beautiful thing on the page, was held at
       72% opacity, so the design was muting its own content
     - the accent appeared about six times on a full homepage
     - every rail was the same size, so the page had one tempo, which is the
       exact criticism the 19 Aug study made of the build this replaced
     - hover did almost nothing, so nothing felt alive under the pointer

   None of what follows adds a filter, a blur, a shadow or an idle animation.
   It is all opacity, transform and colour. */

/* -- 1. let the artwork be the artwork ------------------------------------
   The scrim does the legibility work; the picture does not need dimming to
   make type readable on top of it. */
.slice .bg{ opacity:.94; }
.slice.lead .bg{ opacity:1; }
@media(hover:hover){ .slice:hover .bg, .slice:focus-visible .bg{ opacity:1; } }
/* One vertical ramp doing all the work put the whole scrim between 26% and
   48% of the frame, which reads as a horizon line drawn across the artwork.
   Two passes instead: a gentler bottom-up fade, and a left-hand wash that
   carries the copy — so the type sits on near-solid ground while most of the
   picture survives. Same construction as the title page header. */
.slice::after{
  background:
    linear-gradient(0deg,
      var(--color-bg-deepest) 0%,
      color-mix(in srgb,var(--color-bg-deepest) 88%,transparent) 20%,
      color-mix(in srgb,var(--color-bg-deepest) 54%,transparent) 45%,
      color-mix(in srgb,var(--color-bg-deepest) 18%,transparent) 74%,
      transparent 96%),
    linear-gradient(90deg,
      color-mix(in srgb,var(--color-bg-deepest) 70%,transparent) 0%,
      color-mix(in srgb,var(--color-bg-deepest) 24%,transparent) 42%,
      transparent 70%);
}
/* Tall enough for the lead frame to carry a headline and a synopsis, not so
   tall that the copy sits in the last fifth of a mostly empty picture. */
.hero{ height:clamp(340px,40vw,520px); }
/* the lead frame earns more of the strip. One thing leading per screen was the
   whole point of the direction, and the first cut gave it 37%. */
.slice.lead{ flex:0 0 clamp(340px,44vw,720px); }

/* -- 2. the plate number becomes an editorial device ----------------------
   It was --text-faint at 11px out in the margin: invisible, i.e. a structure
   nobody could see. It is the accent now, with a rule under it, and it is what
   makes a rail read as a numbered plate rather than as a row. */
.railhead .cap::before{
  color:var(--accent-base); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); top:.62em;
}
@media(min-width:1100px){
  .railhead .cap::before{
    padding-bottom:.5em;
    border-bottom:1px solid color-mix(in srgb,var(--accent-base) 45%,transparent);
  }
}
/* the rule under a rail head spans the rail, not a token 340px of it. This is
   most of what gives the page horizontal structure. */
.railhead .cap::after{ width:100%; right:0; opacity:.5; }
.railhead{ display:grid; grid-template-columns:1fr auto; align-items:baseline; }
.railhead .cap{ grid-column:1; }

/* -- 3. cadence: the rails stop being the same rail three times -----------
   Continue Watching is landscape stills of something you are part way into, so
   it should be the biggest thing under the hero. Popular is a browse strip and
   wants density. Recently Added sits between. One shared --card-w across all
   three is why the page had one tempo. */
.cw .addgrid > .cwcard{ flex:0 0 clamp(280px,26vw,440px); width:auto; }
#addgrid > .pcard{ flex:0 0 clamp(150px,13vw,220px); width:auto; }
#popgrid > .pcard{ flex:0 0 clamp(118px,9.5vw,164px); width:auto; }
#popgrid .pt{ font-size:var(--text-sm); }

/* -- 4. hover, with something to say --------------------------------------
   Was a border-colour change. Now the frame takes the safelight, the artwork
   lifts a hair, the caption comes up, and a registration mark strikes the
   corner: the same mark that sits on the featured frames, so the gesture is
   the theme's own rather than a generic highlight. Transform, opacity and
   colour only. Nothing here costs a paint pass. */
.poster{ overflow:hidden; }
.poster img{ transition:transform 140ms var(--ease-standard); }
@media(hover:hover){
  .pcard:hover .poster img, .cwcard:hover .cw-thumb img{ transform:scale(1.035); }
  .pcard:hover .poster{ border-color:var(--accent-base); box-shadow:inset 0 0 0 1px var(--accent-base); }
  .pcard:hover .pt{ color:var(--safelight-text); }
  .cwcard:hover .cw-t{ color:var(--safelight-text); }
}
.pt, .cw-t{ transition:color 100ms var(--ease-standard); }
.poster::before{
  z-index:4; background-color:var(--color-bg-deep);
  background-image:url("/static/brand/registration.svg");
  background-repeat:no-repeat; background-position:8px 8px; background-size:14px 14px;
  opacity:0; transition:opacity 110ms var(--ease-standard);
}
.pcard:hover .poster::before, .poster:focus-visible::before{ opacity:1; background-color:transparent; }

/* -- 5. the strip says what is happening ----------------------------------
   A status line that only ever reads "all systems live" is furniture. The
   stream count is the one number on this page that changes minute to minute,
   and it is already fetched, so mountStatusStrip mirrors it rather than
   adding a request. */
.mf-strip{
  padding:7px var(--bleed-inset);
  color:var(--text-secondary);
  font-size:var(--text-2xs); letter-spacing:var(--track-caps-sm);
}
.mf-strip .f b{ color:var(--lighttable-text); }
.mf-strip #mf-streams-f b{ color:var(--safelight-text); }
.mf-strip .sp b{ color:var(--text-primary); font-variant-numeric:tabular-nums; }

/* -- 6. the shared element, retimed ---------------------------------------
   The poster-to-detail morph was already wired and is the best moment in the
   app; at 420ms on the old easing it read as a slow zoom rather than as the
   card opening. */
@media(prefers-reduced-motion:no-preference){
  ::view-transition-group(film-poster){ animation-duration:300ms; animation-timing-function:var(--ease-enter); }
}


/* ═══ D18 — THE MATERIAL PASS ═══════════════════════════════════════════════
   Written after looking at the deployed site rather than the harness.
   Operator's verdict: "a bit too blocky and cheap feeling. The site is missing
   luxury with the redesign." Correct on all three counts, and the cause is one
   mistake made three times.

   I equated ARTISAN with REDUCED TO STRUCTURE. So: every radius went to 0–3px,
   every shadow was deleted, every surface became a flat fill with an identical
   1px hairline, and a literal film motif got applied to every divider on the
   page. Each of those was defensible on its own and together they produce a
   technical drawing — which is a real register, but it is the Adult Swim
   register, and that theme is *deliberately* cheap-looking because that is the
   joke. Darkroom was not supposed to be in on it.

   What luxury actually needs, none of which is decoration:

     MATERIAL  a surface that reads as lit — a catch-line along the top edge
               where it would face a lamp, a soft dark shadow beneath it where
               it would occlude the ground. Not a glow. Never a glow.
     GEOMETRY  6–8px. Zero radius plus a hairline plus no shadow is also
               exactly what an unstyled element looks like, which is why it
               read as unfinished rather than as considered.
     LIGHT     a ground with a direction to it, so the page has a top.
     RESTRAINT in ornament specifically — which is the one place the first cut
               had none. See below.

   Everything here is a gradient, a radius or a shadow. No new element, no new
   animation, no filter, no blur. */


/* ── 1. THE SPROCKETS GO ───────────────────────────────────────────────────
   They were the single most damaging thing on the page and they were my idea,
   so they get named. Six per screen: around the hero, under every rail
   heading, in the footer flourish, on the gate. At render size a row of small
   tan squares does not read as film perforation — it reads as a dotted border
   from a 2003 template, and it is muddy brown against a blue-dark field.

   The instinct was right and the execution was literal. A film archive should
   feel like one; it should not be *labelled* as one on every divider. What
   replaces them is a rule that fades — which is what a considered divider
   looks like in any medium. */
.mf-sprocket, .gh-rule{
  height:1px; background-image:none;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--accent-base) 55%,transparent) 0,
    var(--border-default) 22%,
    transparent 92%);
  opacity:1;
}
#heroshell::before, #heroshell::after{ display:none; }
.railhead .cap::after{
  height:1px; bottom:calc(-1 * var(--sp-3)); width:100%; right:0; opacity:1;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--accent-base) 70%,transparent) 0 44px,
    var(--border-default) 44px,
    transparent 78%);
}
/* the footer mark becomes the aperture rather than a strip of perforations —
   one small drawn thing, which is what a colophon rule wants */
.flourish i{
  width:16px; max-width:16px; height:16px; background:var(--accent-base);
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
  mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
  opacity:.75;
}
.flourish{ gap:16px; }


/* ── 2. SURFACES BECOME OBJECTS ────────────────────────────────────────────
   Radius comes from the tokens rather than a literal, so Adult Swim — which
   overrides those tokens back to 0–2px — stays square without a single
   duplicated rule. */
.poster, .cw-thumb{
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-sm), var(--edge-highlight);
  transition:border-color 90ms var(--ease-standard),
             box-shadow 140ms var(--ease-standard),
             transform 140ms var(--ease-standard);
}
@media(hover:hover){
  .pcard:hover .poster, .cwcard:hover .cw-thumb{
    box-shadow:var(--shadow-lg), var(--edge-highlight-strong);
  }
}
.slice{ border-radius:var(--radius-lg); overflow:hidden; }
.npc, .lb, .lbox, .loginbox, .pv-box, .kbd-panel, .ctx-menu, .menu, .cmdk-panel{
  border-radius:var(--radius-xl);
  box-shadow:var(--shadow-modal);
}
.npc{ box-shadow:var(--shadow-md), var(--edge-highlight); }
.sheet-close, .back-to-top, .kbd-help-btn, .railnav{
  border-radius:var(--radius-md); box-shadow:var(--shadow-sm), var(--edge-highlight);
}
.btn, .lbtn, .btn--sm, .btn:not(.btn--accent), .search, .field, .searchfield,
.iconbtn, .chip, .badge, .npc .badge, .view-toggle, .view-toggle button,
.menu-item, .ctx-item, .libtab, .reqchip, .tp-chip, .pv-btn.btn, .lb-poster,
input[type="text"], input[type="password"], input[type="search"], select.field{
  border-radius:var(--radius-md);
}
.libtab{ border-radius:var(--radius-sm) var(--radius-sm) 0 0; }
.avatar{ border-radius:50%; }
.pstatus{ border-radius:var(--radius-lg) 0 var(--radius-md) 0; }
.cw-bar, .cw-bar i, .npc .bar, .npc .bar i{ border-radius:var(--radius-pill); }
.live::before, .tally, .chip .sd, .sname .dot{ border-radius:var(--radius-xs); }
:where(a,button,input,select,textarea,[tabindex]):focus-visible{ border-radius:var(--radius-sm); }

/* the topbar stops being a hairline on nothing and becomes a slab with a
   shadow under it — which is also what tells you the page scrolls beneath it */
#topbar{
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--color-bg-surface) 70%,var(--color-bg-deepest)),
    color-mix(in srgb,var(--color-bg-deepest) 92%,transparent));
  box-shadow:0 1px 0 rgba(0,0,0,.5), 0 12px 28px -18px rgba(0,0,0,.9);
}
.mf-strip{ background:#020a0e; }


/* ── 3. THE PRIMARY ACTION GETS A MATERIAL ─────────────────────────────────
   It was a flat #B3BFB8 rectangle. A single mid-tone fill with a square corner
   is, precisely, what an unstyled <button> looks like — so the one control
   that should have read as the most considered thing on the page read as the
   least. Same colour family, given a fall and a catch-line. */
.btn--accent, button.lbtn, .lplay-go{
  background:var(--brass); background-color:transparent;
  color:var(--brass-ink); border:0;
  box-shadow:var(--brass-shadow), var(--brass-edge);
  font-weight:var(--fw-semibold);
  border-radius:var(--radius-md);
}
.btn--accent:hover, button.lbtn:hover, .lplay-go:hover{
  background:var(--brass-hover); background-color:transparent;
  color:var(--brass-ink); border:0;
  box-shadow:var(--brass-shadow), var(--brass-edge);
  transform:translateY(-1px);
}
.btn--accent:active, button.lbtn:active, .lplay-go:active{
  transform:translateY(1px); box-shadow:inset 0 2px 6px rgba(0,0,0,.4);
}
/* secondary buttons get a surface instead of an outline on nothing */
.btn:not(.btn--accent), .lbtn:not(.lplay-go):not(button){
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--text-primary) 8%,transparent),
    color-mix(in srgb,var(--text-primary) 4%,transparent));
  border:1px solid var(--border-default);
  box-shadow:var(--edge-highlight);
  color:var(--text-primary);
}
.btn:not(.btn--accent):hover{
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--text-primary) 13%,transparent),
    color-mix(in srgb,var(--text-primary) 7%,transparent));
  border-color:var(--border-strong);
}


/* ── 4. THE LOADING STATE STOPS LOOKING BROKEN ─────────────────────────────
   Measured on the live site: 30 of 58 posters were still loading at the moment
   I looked, and the "unexposed frame" I designed for that state is a flat box
   with a hairline. Thirty of those at once does not read as an archive waiting
   to be exposed; it reads as a page that has failed. Honest is not the same as
   good. A plate with a fall of light in it reads as a plate. */
.poster::before{
  background-image:linear-gradient(155deg,
    color-mix(in srgb,var(--color-bg-elevated) 92%,transparent) 0%,
    var(--color-bg-deep) 58%,
    #02090d 100%);
  background-color:transparent;
  box-shadow:var(--edge-highlight);
}
.pcard:hover .poster::before, .poster:focus-visible::before{
  background-image:url("/static/brand/registration.svg");
  background-repeat:no-repeat; background-position:9px 9px; background-size:14px 14px;
  box-shadow:none;
}


/* ── 5. THE CHROME QUIETENS AND THE TYPE GROWS ─────────────────────────────
   The first cut ran nearly every label at 11px mono caps in grey. A handful of
   those is instrumentation; forty of them is a spreadsheet. They are a step
   larger, a step warmer, and the ones that were never data have gone back to
   the reading face. */
.py, .cw-meta, .npc .meta, .npc .se, .slice .meta, .libcount, .foot-whisper,
.mf-strip, .mf-colophon, .lmeta{ font-size:var(--text-2xs); }
.pt{ font-size:var(--text-base); }
.cw-t{ font-size:var(--text-base); }
/* these are prose, not readouts, and should never have been mono caps */
.railhead .btn--sm, .railhead .reqline, .libstatus{
  font-family:var(--font-sans); font-size:var(--text-sm);
  letter-spacing:0; text-transform:none;
}
.foot-h, .menu-label{ font-size:var(--text-2xs); color:var(--text-muted); }
/* the plate number was a stray artefact at 11px in the margin. Bigger, on the
   brass, and set against a rule so it reads as a plate mark. */
.railhead .cap::before{ font-size:var(--text-2xs); }

/* more air. Luxury is mostly space, and the first cut was dense. */
.addgrid{ gap:26px; }
@media(min-width:1100px){
  .cw, .now, .added, #personal > section{ margin-bottom:var(--sp-16); }
  .railhead{ margin-bottom:var(--sp-6); }
}


/* ── 6. THE HERO SOFTENS ───────────────────────────────────────────────────
   Frames butted edge to edge on a 1px line meant two unrelated pieces of
   artwork colliding on a hard seam — which is what a collage looks like, not
   a contact strip. A real gap, rounded frames, and the strip sits on the
   ground rather than being cut out of it. */
.hero{
  gap:14px; padding:0 var(--bleed-inset);
  background:none; border:0;
  /* No edge mask. It existed to hide a hard cut where the strip met the
     gutter; with real gaps and rounded frames there is no hard cut to hide,
     and the mask was fading the lead frame's own headline whenever the rail
     was scrolled even slightly. */
  -webkit-mask-image:none; mask-image:none;
}
/* The strip now has a real gutter, so snap has to respect it — without this
   the first frame snapped to x:0 and sat half under the page edge, which is
   what put the prev arrow on top of the lead headline. */
.hero{ scroll-padding-inline:var(--bleed-inset); }
/* and the arrows live in the gutter, never over artwork */
#heroshell .railnav{ z-index:6; }
#heroshell .railnav--prev{ left:10px; }
#heroshell .railnav--next{ right:10px; }
.slice{
  box-shadow:var(--shadow-lg), var(--edge-highlight);
  border:1px solid var(--border-subtle);
}
@media(hover:hover){
  .slice:hover, .slice:focus-visible{
    box-shadow:var(--shadow-xl), var(--edge-highlight-strong);
    border-color:var(--border-default);
  }
}
.slice .in::before{ left:0; width:2px; border-radius:2px; opacity:0; }
.slice.lead .in::before{ opacity:.7; }
/* the hero was showing a real scrollbar under the strip; every other rail on
   the page hides its own, and this one is the most visible surface of all */
.hero{ scrollbar-width:none; -ms-overflow-style:none; }
.hero::-webkit-scrollbar{ display:none; height:0; }

/* ── 7. THE CAPTION IS A UNIT AGAIN ────────────────────────────────────────
   The two-line title reserve existed to stop meta rows going ragged across a
   rail, and it worked — but seen against real titles, most of which are one
   line, it opened a visible hole between every title and its own metadata.
   A card whose two halves have drifted apart looks unfinished, and that costs
   more than a slightly uneven baseline across six cards does. Natural height,
   tight gap: the meta belongs to the title, not to the row. */
.pt{ height:auto; min-height:0; line-height:1.3; margin-top:.75rem; }
.py{ margin-top:.2rem; }
.cw-info{ padding-top:.75rem; }
.cw-meta{ margin-top:.2rem; }


/* ═══ D14 — REDUCED MOTION, ONE PLACE, LAST ═════════════════════════════════ */
@media(prefers-reduced-motion:reduce){
  .addgrid > .pcard, .addgrid > .cwcard, .libgrid > .pcard,
  .poster img, .cw-thumb img, .slice .bg,
  .live::after, .pcard::after, #boot img{ animation:none !important; }
  /* a pulse frozen mid-scale is an artefact, not a still — hide it */
  .live::after{ display:none; }
  .pcard, .poster, .cwcard, .cw-thumb, .btn, .lbtn, .navlink::after,
  .slice .bg, .poster::after, .poster img, .poster::before,
  .pt, .cw-t{ transition:none !important; }
  ::view-transition-group(film-poster){ animation:none !important; }
  ::view-transition-old(root), ::view-transition-new(root){ animation:none !important; }
}

/* Forced colors — nothing here relies on a clipped text fill any more, but the
   masked marks do need a fallback since a mask has no colour of its own. */
@media(forced-colors:active){
  .brandmark{ background:CanvasText; }
  .tally{ background:CanvasText; }
  .mf-sprocket, .flourish i, .railhead .cap::after,
  #heroshell::before, #heroshell::after{ background-image:none; }
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D19 — THE OVERLAY SYSTEM                                                 ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   Every dialog on the site inherited the same two mistakes. The scrim was a
   flat 72% wash with `backdrop-filter:blur(14px) saturate(150%)` over the
   whole viewport — a full-screen live blur for a effect that, at 72% opacity,
   nobody can see. And the panel was `--glass-card-bg` with a second blur on
   top of the first. Two stacked live-blur layers to render a dark rectangle.

   Both go. The scrim is painted: a pool of light under the panel and a deep
   vignette at the edges, so a dialog reads as something lit on a table rather
   than a rectangle floating on a dimmed page. The panel is opaque.

   The signature: every panel carries a one-pixel safelight rule across its top
   edge, the same warm line that runs under a rail head. It is the one mark
   that says these surfaces belong to the same object.                        */

.overlay, .cmdk, .kbd-overlay{
  background:
    radial-gradient(ellipse 70% 55% at 50% 44%,
      color-mix(in srgb,var(--color-bg-deepest) 74%,transparent) 0%,
      color-mix(in srgb,#020a0e 88%,transparent) 58%,
      rgba(1,6,9,.94) 100%);
  backdrop-filter:none; -webkit-backdrop-filter:none;
}

/* One panel recipe. `.sheet` is on every dialog box in the markup, so this is
   the single place the material is decided. */
.sheet{
  background:var(--color-bg-surface);
  backdrop-filter:none; -webkit-backdrop-filter:none;
  border:1px solid var(--border-default);
  border-radius:var(--radius-xl);
  box-shadow:var(--shadow-xl), var(--edge-highlight), 0 0 0 1px rgba(0,0,0,.55);
  isolation:isolate;
}
/* the safelight rule. ::before rather than a border so it survives the radius
   and can be inset from the corners, which is what keeps it from reading as a
   coloured border. */
.sheet::before{
  content:""; position:absolute; z-index:4; pointer-events:none;
  left:var(--radius-xl); right:var(--radius-xl); top:0; height:1px;
  background:linear-gradient(90deg,
    transparent 0,
    color-mix(in srgb,var(--accent-base) 62%,transparent) 12%,
    color-mix(in srgb,var(--accent-base) 62%,transparent) 34%,
    color-mix(in srgb,var(--mf-gelatin) 22%,transparent) 60%,
    transparent 100%);
}

/* The close control was a 32px circle with a 6%-white fill: an unstyled
   button in a circle. It becomes a plain glyph that only grows a pad when you
   reach for it — the same behaviour as every other icon control on the site. */
.sheet-close{
  width:34px; height:34px; border-radius:var(--radius-md);
  background:transparent; border:1px solid transparent;
  color:var(--text-muted); box-shadow:none;
  top:14px; right:16px;
  transition:color var(--dur-fast) var(--ease-standard),
             background-color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard);
}
.sheet-close:hover{
  color:var(--text-primary);
  background:color-mix(in srgb,var(--text-primary) 9%,transparent);
  border-color:var(--border-default);
}
.sheet-close:active{ background:color-mix(in srgb,var(--text-primary) 14%,transparent); }

/* Entrance. The scrim fades; the panel arrives from 6px down at 0.986 — small
   enough that it reads as weight settling rather than a zoom. */
@keyframes mf-scrim{ from{opacity:0} to{opacity:1} }
@keyframes mf-sheet{
  from{ opacity:0; transform:translateY(6px) scale(.986); }
  to  { opacity:1; transform:none; }
}
.overlay.on, .cmdk.on, .cmdk.open{ animation:mf-scrim var(--dur-base) var(--ease-standard) both; }
.overlay.on > .sheet, .cmdk.on .palette, .cmdk.open .palette{
  animation:mf-sheet 220ms var(--ease-enter) both;
}
@media(prefers-reduced-motion:reduce){
  .overlay.on, .cmdk.on, .cmdk.open,
  .overlay.on > .sheet, .cmdk.on .palette, .cmdk.open .palette{ animation:none; }
}

/* Section headings inside a panel. Panels were using bare <h3> at body size,
   so a dialog opened with no visible title hierarchy at all. */
.ltitle{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(21px,2.1vw,27px); line-height:1.16; letter-spacing:-.012em;
  color:var(--text-primary); text-shadow:none;
  margin:0 44px var(--sp-2) 0;
}
.lsub{
  margin:0 0 var(--sp-5); max-width:52ch;
  font-size:var(--text-sm); line-height:1.55; color:var(--text-secondary);
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D20 — THE PROJECTION (the player)                                        ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   This surface had never been designed. It was a 960px box with a flat black
   control strip along the bottom, eight identical bordered squares in a row,
   and a native range input for volume. It is the one place on the site where
   a member spends an hour rather than four seconds, and it looked like the
   default `<video controls>` with a border drawn around it.

   The idea is a projection. The stage is black and as large as the window
   allows. The chrome is not a bar — it is two gradients that the controls
   live inside, so nothing has a hard edge and the picture is never boxed in.
   One control is solid: the play button, in brass, because it is the only
   thing you look for in the dark. Everything else is a glyph that grows a pad
   when you reach for it.                                                     */

#playerlightbox{ padding:clamp(10px,2.2vw,44px); }

.pv-box{
  background:#01070a;
  max-width:min(1520px,95vw); width:100%; max-height:94vh;
  border:1px solid rgba(179,191,184,.10);
  border-radius:var(--radius-lg);
  overflow:hidden;
  box-shadow:
    var(--shadow-xl),
    0 0 0 1px rgba(0,0,0,.7),
    0 0 140px -40px rgba(169,113,75,.20);
}
/* the safelight rule belongs on panels, not over film */
.pv-box::before{ display:none; }

.pv-stage{ background:#000; overflow:hidden; }
.pv-stage video{ background:#000; }

/* Two scrims. The top one carries the title and close; the bottom one is the
   control surface itself. Both fade with the controls, so an idle player is
   nothing but the picture. */
.pv-stage::before{
  content:""; position:absolute; left:0; right:0; top:0; z-index:4;
  height:clamp(72px,9vw,128px); pointer-events:none;
  background:linear-gradient(180deg,
    rgba(0,0,0,.80) 0%, rgba(0,0,0,.44) 44%, rgba(0,0,0,.12) 78%, transparent 100%);
  transition:opacity var(--dur-base) var(--ease-standard);
}
.pv-stage.controls-hidden::before{ opacity:0; }

.pv-title{
  top:clamp(13px,1.5vw,20px); left:clamp(16px,2vw,26px); right:78px;
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(17px,1.5vw,23px); line-height:1.2; letter-spacing:-.008em;
  color:#f3f6f3; text-shadow:0 1px 16px rgba(0,0,0,.75);
}
.pv-stage.has-backep .pv-title{ top:clamp(44px,4.4vw,56px); }

.pv-close, .pv-backep{
  background:transparent; border:1px solid transparent; box-shadow:none;
  color:rgba(238,244,240,.72); border-radius:var(--radius-md);
}
.pv-close{ top:clamp(11px,1.3vw,18px); right:clamp(12px,1.6vw,22px); width:36px; height:36px; }
.pv-backep{ top:clamp(11px,1.3vw,18px); left:clamp(12px,1.6vw,22px);
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase; padding:7px 11px; }
.pv-close:hover, .pv-backep:hover{
  color:#fff; background:rgba(238,244,240,.14); border-color:transparent;
}

/* ── the control surface ───────────────────────────────────────────────── */
.pv-controls{
  padding:clamp(44px,5.5vw,78px) clamp(14px,1.9vw,26px) clamp(12px,1.3vw,20px);
  background:linear-gradient(0deg,
    rgba(0,0,0,.90) 0%, rgba(0,0,0,.66) 34%, rgba(0,0,0,.26) 68%, transparent 100%);
  border:0; display:flex; flex-direction:column; gap:clamp(4px,.5vw,8px);
}
.pv-row{ gap:clamp(6px,.8vw,12px); }
.pv-row:first-child{ padding:0 2px; }

/* ── the seek track ────────────────────────────────────────────────────── */
.pv-track{ height:28px; }
.pv-rail{
  height:3px; border-radius:var(--radius-pill);
  background:rgba(231,236,232,.13);
  transition:height 120ms var(--ease-standard);
}
.pv-track:hover .pv-rail, .pv-track:focus-visible .pv-rail{ height:6px; }
.pv-buffered{ background:rgba(231,236,232,.34); border-radius:var(--radius-pill); }
.pv-played{
  border-radius:var(--radius-pill);
  background:linear-gradient(90deg,#a86f45 0%,#c68b5a 62%,#dda875 100%);
  box-shadow:0 0 14px -3px rgba(217,160,109,.55);
}
/* The knob is absent until you reach for the track. A permanent dot on a
   progress bar is the single most common tell of an untouched player. */
.pv-knob{
  width:13px; height:13px; background:#f2e6da; border:0;
  box-shadow:0 0 0 1px rgba(0,0,0,.55), 0 2px 7px rgba(0,0,0,.65);
  transform:translate(-50%,-50%) scale(0);
  transition:transform 150ms var(--ease-spring);
}
.pv-track:hover .pv-knob, .pv-track:focus-visible .pv-knob,
.pv-track:active .pv-knob{ transform:translate(-50%,-50%) scale(1); }
.pv-track:focus-visible{ outline:none; }
.pv-track:focus-visible .pv-rail{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent-base) 34%,transparent);
}

/* ── the transport ─────────────────────────────────────────────────────── */
.pv-btn, .pv-btn.btn{
  width:38px; height:38px; padding:0;
  border-radius:var(--radius-md);
  background:transparent; border:1px solid transparent; box-shadow:none;
  color:rgba(238,244,240,.80); opacity:1;
  transition:color var(--dur-fast) var(--ease-standard),
             background-color var(--dur-fast) var(--ease-standard),
             transform var(--dur-fast) var(--ease-standard);
}
.pv-btn:hover, .pv-btn.btn:hover{
  color:#fff; background:rgba(238,244,240,.13); border-color:transparent;
}
.pv-btn:active{ transform:scale(.94); }
.pv-btn svg{ width:19px; height:19px; }

/* the one solid object */
#pv-playbtn, #pv-playbtn.btn{
  width:46px; height:46px; border-radius:50%; margin-right:2px;
  background:var(--brass); background-color:transparent;
  color:var(--brass-ink); border:0;
  box-shadow:var(--brass-shadow), var(--brass-edge);
}
#pv-playbtn:hover, #pv-playbtn.btn:hover{
  background:var(--brass-hover); background-color:transparent;
  color:var(--brass-ink); transform:translateY(-1px);
}
#pv-playbtn:active{ transform:translateY(1px) scale(.97); }
#pv-playbtn svg{ width:21px; height:21px; }

.pv-time{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:rgba(238,244,240,.76);
  padding-left:6px; margin-right:2px;
}
#pv-dur{ color:rgba(238,244,240,.44); }

/* ── volume ────────────────────────────────────────────────────────────── */
.pv-vol{
  -webkit-appearance:none; appearance:none;
  width:84px; height:28px; background:transparent; border:0; padding:0;
}
.pv-vol:focus{ outline:none; }
.pv-vol::-webkit-slider-runnable-track{
  height:3px; border-radius:var(--radius-pill); background:rgba(238,244,240,.22);
}
.pv-vol::-webkit-slider-thumb{
  -webkit-appearance:none; width:11px; height:11px; margin-top:-4px;
  border-radius:50%; background:#eef4f0; border:0;
  box-shadow:0 0 0 1px rgba(0,0,0,.5), 0 1px 4px rgba(0,0,0,.6);
}
.pv-vol::-moz-range-track{
  height:3px; border-radius:var(--radius-pill); background:rgba(238,244,240,.22);
}
.pv-vol::-moz-range-thumb{
  width:11px; height:11px; border:0; border-radius:50%; background:#eef4f0;
  box-shadow:0 0 0 1px rgba(0,0,0,.5), 0 1px 4px rgba(0,0,0,.6);
}
.pv-vol:focus-visible::-webkit-slider-thumb{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent-base) 40%,transparent);
}

/* ── the player's own menus ────────────────────────────────────────────── */
.pv-menu{
  bottom:clamp(64px,7vw,86px); right:clamp(14px,1.9vw,26px);
  min-width:216px; max-height:min(320px,46vh); padding:6px;
  background:color-mix(in srgb,var(--color-bg-deepest) 96%,transparent);
  border:1px solid var(--border-default);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-xl), var(--edge-highlight);
}
.pv-menu button{
  border-radius:var(--radius-sm);
  padding:8px 12px 8px 28px; position:relative;
  font-size:var(--text-sm); line-height:1.35; color:var(--text-secondary);
}
.pv-menu button:hover{
  background:color-mix(in srgb,var(--text-primary) 9%,transparent);
  color:var(--text-primary);
}
.pv-menu button[aria-pressed="true"]{
  color:var(--safelight-text); font-weight:var(--fw-medium);
}
.pv-menu button[aria-pressed="true"]::before{
  content:""; position:absolute; left:11px; top:1.05em;
  width:5px; height:5px; margin-top:-2px; border-radius:50%;
  background:var(--accent-base);
}
.pv-menu-sub{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); color:var(--text-faint);
}

/* ── loading and failure are two different states ──────────────────────── */
.pv-loading{
  background:radial-gradient(ellipse 46% 46% at 50% 50%,
    rgba(4,21,31,.72), rgba(0,0,0,.86));
  flex-direction:column; gap:var(--sp-4);
}
.pv-loading:not(.is-error){ font-size:0; color:transparent; }
.pv-loading:not(.is-error)::before{
  content:""; display:block; width:46px; height:46px;
  background:var(--accent-base);
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
          mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
  animation:mf-iris-spin 2.4s linear infinite;
}
@keyframes mf-iris-spin{ to{ transform:rotate(360deg); } }
@media(prefers-reduced-motion:reduce){
  .pv-loading:not(.is-error)::before{ animation:none; opacity:.7; }
}
.pv-loading.is-error{
  font-family:var(--font-sans); font-size:var(--text-sm);
  color:var(--text-secondary); text-align:center;
  padding:0 clamp(20px,6vw,80px); line-height:1.6;
}
.pv-loading.is-error::before{
  content:""; display:block; width:26px; height:26px; margin:0 auto var(--sp-1);
  background:var(--safelight-text);
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
          mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
  opacity:.6;
}

/* ── the episode picker shares the player's box ────────────────────────── */
.pv-picker{
  background:var(--color-bg-surface);
  padding:clamp(22px,2.6vw,34px) clamp(20px,2.6vw,34px) clamp(26px,3vw,38px);
}
.pv-seasons{ gap:var(--sp-2); margin-bottom:var(--sp-5); }
.pv-season-btn{
  border-radius:var(--radius-md);
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--text-primary) 7%,transparent),
    color-mix(in srgb,var(--text-primary) 3%,transparent));
  border:1px solid var(--border-default); color:var(--text-secondary);
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  padding:7px 13px; box-shadow:var(--edge-highlight);
}
.pv-season-btn:hover{ color:var(--text-primary); border-color:var(--border-strong); }
.pv-season-btn[aria-pressed="true"]{
  background:var(--brass); color:var(--brass-ink); border-color:transparent;
  box-shadow:var(--brass-shadow), var(--brass-edge);
}
.pv-eplist{ gap:var(--sp-2); }

@media(min-width:641px) and (max-width:720px){
  .pv-box{ max-width:100%; border-radius:var(--radius-md); }
  #pv-playbtn, #pv-playbtn.btn{ width:42px; height:42px; }
  .pv-btn, .pv-btn.btn{ width:36px; height:36px; }
  .pv-btn svg{ width:18px; height:18px; }
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D21 — THE TITLE PAGE                                                     ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   The detail modal is the most-opened surface on the site and it was laid out
   as one column of paragraphs under a picture. Two things were wrong with it
   beyond decoration.

   The first was a seam. The header's scrim faded to `--modal-grad-2` and the
   body sat on `--glass-card-bg`; the two never matched, so a hard horizontal
   line ran across the middle of every title in the library. It fades to the
   panel's own surface colour now, and the join is invisible.

   The second was measure. The synopsis ran the full 1040px of the modal —
   about 150 characters a line, roughly twice a readable measure — while the
   right two-fifths of the header sat empty. It is two columns now: the prose
   keeps a measure, and everything that is a fact rather than a sentence moves
   into a rail beside it, set as data.                                        */

.lbox{
  max-width:min(1160px,96vw);
  max-height:min(920px,92vh);
  background:var(--color-bg-surface);
}
.lbox::before{ z-index:6; }

/* ── the header ────────────────────────────────────────────────────────── */
/* `.lb-art--poster` — the fallback for a title with no backdrop on file — is
   the poster at scale(1.15) under a 22px blur. `.lb-hero` never clipped, so
   that scaled, blurred layer spilled about a hundred pixels past the header
   and laid a pale haze across the top of the body copy. Every title without a
   backdrop had a bright band running across it. This is the whole fix. */
.lb-hero{ min-height:clamp(250px,32vw,400px); overflow:hidden; }
.lb-art{ background-position:center 22%; }
/* Three passes, in order: a bottom-up fade that lands exactly on the panel
   surface, a left wash so a bright backdrop still carries a title, and a
   corner vignette that keeps the art from touching the panel edge. */
.lb-hero::after{
  background:
    linear-gradient(0deg,
      var(--color-bg-surface) 0%,
      var(--color-bg-surface) 13%,
      color-mix(in srgb,var(--color-bg-surface) 58%,transparent) 36%,
      color-mix(in srgb,var(--color-bg-surface) 14%,transparent) 68%,
      transparent 100%),
    linear-gradient(90deg,
      color-mix(in srgb,var(--color-bg-deepest) 70%,transparent) 0%,
      color-mix(in srgb,var(--color-bg-deepest) 26%,transparent) 36%,
      transparent 64%),
    linear-gradient(180deg,
      color-mix(in srgb,var(--color-bg-deepest) 46%,transparent) 0%,
      transparent 38%);
}
.lb-heroin{
  padding:var(--sp-6) clamp(22px,3.2vw,44px) clamp(20px,2.6vw,32px);
  gap:clamp(18px,2.4vw,32px);
}
.lb-poster{
  width:clamp(104px,10.5vw,150px);
  border-radius:var(--radius-lg);
  border:1px solid rgba(238,244,240,.16);
  box-shadow:var(--shadow-lg), var(--edge-highlight);
  background:var(--color-bg-deep);
}
.lb-herocopy{ max-width:62ch; }
.lb-kicker{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-secondary); margin-bottom:var(--sp-2);
}
.lbox .ltitle{
  font-size:clamp(28px,3.6vw,50px); line-height:1.04; letter-spacing:-.02em;
  margin:0 0 var(--sp-3); text-shadow:0 2px 24px rgba(2,9,14,.6);
}
.lrate{ display:flex; gap:var(--sp-2); flex-wrap:wrap; margin-bottom:var(--sp-4); }
.rbadge{
  display:inline-flex; align-items:baseline; gap:6px;
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data);
  color:var(--text-primary);
  padding:5px 9px; border-radius:var(--radius-sm);
  background:color-mix(in srgb,var(--color-bg-deepest) 58%,transparent);
  border:1px solid var(--border-subtle);
}
.rbadge .star{ color:var(--safelight-text); }
.rbadge .rl{ color:var(--text-faint); letter-spacing:var(--track-caps-sm); text-transform:uppercase; }
.lplay{ display:flex; gap:var(--sp-3); flex-wrap:wrap; }

/* ── the body: prose left, facts right ─────────────────────────────────── */
.lbody{
  background:var(--color-bg-surface);
  padding:clamp(22px,2.6vw,34px) clamp(22px,3.2vw,44px) clamp(30px,3.4vw,44px);
}
@media(min-width:900px){
  .lbody{
    display:grid;
    grid-template-columns:minmax(0,1fr) clamp(196px,21%,252px);
    column-gap:clamp(30px,3.6vw,60px);
    align-content:start;
  }
  .lsyn, .lcredits{ grid-column:1; }
  .lgen, .llinks{ grid-column:2; align-self:start; }
  .lb-eps, .lb-similar{ grid-column:1 / -1; }
}
.lsyn{
  margin:0 0 var(--sp-5);
  max-width:66ch;
  font-size:var(--text-md); line-height:1.68; color:var(--text-secondary);
}
/* the opening line gets the paper's own colour, so the eye has a place to
   start — the same trick a printed page uses with a lead-in */
.lsyn::first-line{ color:var(--text-primary); }

/* Credits as a spec list. Label in mono caps above the value, the same voice
   the status strip and the plate numbers use for anything measured. */
.lcredits{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr));
  gap:var(--sp-4) clamp(20px,2.4vw,36px);
  margin:0 0 var(--sp-2); padding-top:var(--sp-5);
  border-top:1px solid var(--border-subtle);
}
.lcredits .cr{ display:block; min-width:0; }
.lcredits .cr--wide{ grid-column:1 / -1; }
.lcredits b{
  display:block; margin-bottom:5px; font-weight:var(--fw-regular);
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}
.lcredits i{
  display:block; font-style:normal;
  font-size:var(--text-sm); line-height:1.55; color:var(--text-primary);
}

/* Genres: a labelled stack, not a run-on middot line. */
.lgen{
  display:flex; flex-wrap:wrap; align-items:flex-start; gap:6px;
  margin:0 0 var(--sp-6);
}
.lgen::before{
  content:"Genre"; flex:0 0 100%;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin-bottom:2px;
}
.lgen{ padding-left:11px; }
.lgen::before{ padding-left:0; }
.lgen span{
  font-size:var(--text-sm); color:var(--text-secondary);
  padding:4px 10px; border-radius:var(--radius-sm);
  background:color-mix(in srgb,var(--text-primary) 5%,transparent);
  border:1px solid var(--border-subtle);
}

/* External links: a column of quiet rows with a trailing arrow, not three
   buttons in a line competing with Play. */
.llinks{ display:flex; flex-direction:column; gap:2px; margin:0; }
.llinks::before{
  content:"Elsewhere"; padding-left:11px;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin-bottom:6px;
}
.llinks a.lbtn, .llinks a.lbtn:not(.lplay-go):not(button){
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  width:100%; padding:9px 11px; border-radius:var(--radius-sm);
  background:transparent; border:1px solid transparent; box-shadow:none;
  font-size:var(--text-sm); color:var(--text-secondary); text-decoration:none;
}
.llinks a.lbtn::after{ content:"↗"; color:var(--text-faint); font-size:var(--text-xs); }
.llinks a.lbtn:hover, .llinks a.lbtn:not(.lplay-go):not(button):hover{
  background:color-mix(in srgb,var(--text-primary) 7%,transparent);
  border-color:var(--border-subtle); color:var(--text-primary);
}
.llinks a.lbtn:hover::after{ color:var(--safelight-text); }

/* ── section heads inside the title page ───────────────────────────────── */
.lb-caphead{
  display:flex; align-items:center; gap:var(--sp-3);
  margin:var(--sp-8) 0 var(--sp-4);
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted);
}
.lb-caphead::after{
  content:""; flex:1; height:1px;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--accent-base) 46%,transparent) 0,
    var(--border-subtle) 26%, transparent 88%);
}
.lb-eps:empty, .lb-similar:empty{ display:none; }

/* ── episode rows (shared with the player's own picker) ────────────────── */
.pv-eplist{ gap:var(--sp-2); }
.pv-ep{
  gap:clamp(12px,1.4vw,18px); padding:10px;
  border-radius:var(--radius-lg);
  border:1px solid transparent;
  background:color-mix(in srgb,var(--text-primary) 3.5%,transparent);
  box-shadow:none;
  transition:background-color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard),
             transform var(--dur-fast) var(--ease-standard);
}
.pv-ep:hover{
  background:color-mix(in srgb,var(--text-primary) 7%,transparent);
  border-color:var(--border-default);
  transform:translateX(2px);
}
.pv-ep-thumb{
  flex:0 0 clamp(112px,11vw,164px); width:auto;
  border-radius:var(--radius-md);
  border:1px solid var(--border-subtle);
  box-shadow:var(--shadow-sm);
}
.pv-ep-title{
  font-size:var(--text-base); font-weight:var(--fw-medium);
  color:var(--text-primary); line-height:1.3;
}
.pv-ep-title .se{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:var(--safelight-text);
  margin-right:9px;
}
.pv-ep-meta{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin-top:5px;
}
.pv-ep-ov{ color:var(--text-secondary); margin-top:7px; line-height:1.5; }
.pv-ep-resume{
  height:2px; border-radius:var(--radius-pill); margin-top:10px;
  background:color-mix(in srgb,var(--text-primary) 12%,transparent);
}
.pv-ep-resume i{ background:var(--accent-base); }

@media(max-width:640px){
  .lb-heroin{ flex-direction:column; align-items:flex-start; }
  .lb-poster{ width:96px; }
  .lbox .ltitle{ font-size:clamp(24px,7vw,32px); }
  .pv-ep-thumb{ flex-basis:96px; }
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D22 — THE COMMAND BAR                                                    ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   It opened 100px from the top of the window at 640px wide with no shadow,
   which put it in the exact place and shape a browser's own find-in-page bar
   occupies. Anything that can be mistaken for browser chrome is not a
   designed surface. It moves down into the page, gets width, gets weight,
   and gets a selected-row treatment strong enough to follow with the
   keyboard from across the room.                                            */

.cmdk{ padding:clamp(60px,12vh,150px) 20px 20px; align-items:flex-start; }
.palette{
  width:min(94vw,720px); max-height:min(70vh,640px);
  background:var(--color-bg-surface);
  border:1px solid var(--border-default);
  border-radius:var(--radius-xl);
  box-shadow:var(--shadow-xl), var(--edge-highlight), 0 0 0 1px rgba(0,0,0,.55);
  position:relative;
}
.palette::before{
  content:""; position:absolute; z-index:4; pointer-events:none;
  left:var(--radius-xl); right:var(--radius-xl); top:0; height:1px;
  background:linear-gradient(90deg, transparent 0,
    color-mix(in srgb,var(--accent-base) 62%,transparent) 12%,
    color-mix(in srgb,var(--accent-base) 62%,transparent) 34%,
    color-mix(in srgb,var(--mf-gelatin) 22%,transparent) 60%, transparent 100%);
}

.p-input{
  padding:clamp(15px,1.6vw,20px) clamp(16px,1.8vw,22px);
  border-bottom:1px solid var(--border-subtle);
  background:color-mix(in srgb,var(--color-bg-deep) 55%,transparent);
}
.p-input svg{ width:17px; height:17px; color:var(--text-faint); }
.p-input input{
  font-family:var(--font-sans); font-size:var(--text-md);
  font-weight:var(--fw-regular); color:var(--text-primary);
  padding:7px 0; line-height:1.35;
}
.p-input input::placeholder{ color:var(--text-faint); }
.p-input .esc{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); border:1px solid var(--border-subtle);
  border-radius:var(--radius-xs); padding:4px 7px; background:transparent;
}

.p-group{ padding:var(--sp-2) var(--sp-2) var(--sp-3); }
.p-group + .p-group{ border-top:1px solid var(--border-subtle); }
/* scoped: `.gl` is also the genre-row label inside the instrument, and an
   unscoped rule here quietly restyled a chart three panels away */
.p-group > .gl{
  font-family:var(--font-mono); font-weight:var(--fw-regular);
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm);
  text-transform:uppercase; color:var(--text-faint);
  padding:var(--sp-3) 12px var(--sp-2);
}
.p-item{
  padding:9px 12px; border-radius:var(--radius-md);
  border:1px solid transparent;
  transition:background-color var(--dur-fast) var(--ease-standard),
             border-color var(--dur-fast) var(--ease-standard);
}
.p-item:hover{ background:color-mix(in srgb,var(--text-primary) 6%,transparent); }
/* The selected row is the one thing a keyboard user tracks, so it is not a
   4%-tint difference from its neighbours — it is lit, edged and marked. */
.p-item.sel{
  background:color-mix(in srgb,var(--text-primary) 10%,transparent);
  border-color:var(--border-default);
  box-shadow:inset 3px 0 0 0 var(--accent-base);
}
.p-ico{
  width:30px; height:30px; flex:none; border-radius:var(--radius-sm);
  display:flex; align-items:center; justify-content:center;
  background:color-mix(in srgb,var(--text-primary) 6%,transparent);
  border:1px solid var(--border-subtle);
  color:var(--text-muted); font-size:var(--text-sm);
}
.p-item.sel .p-ico{ color:var(--safelight-text); border-color:var(--border-accent); }
.p-ico svg{ width:15px; height:15px; }
.p-t{ font-size:var(--text-base); color:var(--text-primary); line-height:1.3; }
.p-s{
  font-size:var(--text-xs); color:var(--text-muted); margin-top:2px; line-height:1.4;
}
.p-item .hint{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); opacity:0;
  transition:opacity var(--dur-fast) var(--ease-standard);
}
.p-item.sel .hint, .p-item:hover .hint{ opacity:1; }

/* Nothing found is a state, not an error. */
.p-empty{
  padding:var(--sp-10) var(--sp-5) var(--sp-12); text-align:center;
  font-size:var(--text-sm); color:var(--text-muted); line-height:1.6;
}
.p-empty::before{
  content:""; display:block; width:34px; height:34px; margin:0 auto var(--sp-4);
  background:var(--text-faint);
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
          mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
  opacity:.5;
}

.cmdk-shortcuts .sc{ gap:var(--sp-3); }
.sc-col{
  border:1px solid var(--border-subtle); border-radius:var(--radius-md);
  padding:12px 14px;
  background:color-mix(in srgb,var(--text-primary) 3%,transparent);
}
.sc-col h4{
  font-family:var(--font-mono); font-weight:var(--fw-regular);
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm);
  text-transform:uppercase; color:var(--text-faint); margin-bottom:10px;
}
.sc-row{ font-size:var(--text-xs); color:var(--text-secondary); padding:4px 0; }
.sc-row .keys b{
  font-family:var(--font-mono); font-weight:var(--fw-regular);
  font-size:var(--text-3xs); color:var(--text-muted);
  background:color-mix(in srgb,var(--text-primary) 7%,transparent);
  border:1px solid var(--border-subtle); border-radius:var(--radius-xs);
  padding:3px 6px; line-height:1;
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D23 — THE CATALOG                                                        ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   Three separate widgets on two rows — a rounded search box, two selects and
   a right-floated count — is the shape of an admin filter bar, and it read
   like one. They become a single instrument: one rail, dividers between the
   controls, the live count set as a readout at the end of it. The tab row
   above keeps its own line and gets a real selected state.                   */

.libhead{ align-items:flex-end; margin-bottom:var(--sp-6); }
/* `.libtitle` shipped as a three-stop gradient clipped to the glyphs, with a
   20-second sheen animation looping behind it forever. A clipped gradient
   computes `-webkit-text-fill-color:transparent`, which means it cannot be
   contrast-checked, disappears entirely under forced-colors, and — measured
   against its own darkest stop — was 2.94:1 in the Adult Swim theme. Same
   call as `.sheen` in D15: the page title is solid ink. */
.libtitle{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(34px,5vw,64px); line-height:1; letter-spacing:-.02em;
  color:var(--text-primary);
  background:none; -webkit-background-clip:border-box; background-clip:border-box;
  -webkit-text-fill-color:currentColor; animation:none;
}
html[data-theme="adultswim"] .libtitle{
  color:var(--aurora-teal); -webkit-text-fill-color:var(--aurora-teal);
}

.libtabs{
  gap:var(--sp-1); padding-bottom:0; margin-bottom:var(--sp-4);
  border-bottom:1px solid var(--border-subtle);
}
.libtab{
  position:relative; background:transparent; border:0; box-shadow:none;
  border-radius:var(--radius-sm) var(--radius-sm) 0 0;
  padding:10px 14px 12px;
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted);
  transition:color var(--dur-fast) var(--ease-standard);
}
.libtab:hover{ color:var(--text-primary); background:transparent; }
.libtab::after{
  content:""; position:absolute; left:10px; right:10px; bottom:-1px; height:2px;
  background:var(--accent-base); border-radius:1px;
  transform:scaleX(0); transform-origin:left center;
  transition:transform 180ms var(--ease-enter);
}
.libtab.on, .libtab[aria-selected="true"]{ color:var(--text-primary); background:transparent; }
.libtab.on::after, .libtab[aria-selected="true"]::after{ transform:scaleX(1); }

/* one rail, not three widgets */
.libcontrols{
  display:flex; align-items:stretch; gap:0; flex-wrap:wrap;
  margin-bottom:var(--sp-6);
  border:1px solid var(--border-default);
  border-radius:var(--radius-md);
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--color-bg-surface) 88%,transparent),
    color-mix(in srgb,var(--color-bg-deep) 70%,transparent));
  box-shadow:var(--edge-highlight), var(--shadow-sm);
  overflow:hidden;
}
.libcontrols .searchfield{
  flex:1 1 260px; min-width:0; border:0; border-radius:0; background:transparent;
  box-shadow:none; padding:0 14px; gap:10px;
}
.libcontrols .searchfield svg{ width:16px; height:16px; color:var(--text-faint); }
.libcontrols .searchfield input{
  background:transparent; border:0; outline:none; padding:12px 0;
  font-family:var(--font-sans); font-size:var(--text-sm); color:var(--text-primary);
}
.libcontrols .searchfield input::placeholder{ color:var(--text-faint); }
.libcontrols .field{
  flex:0 0 auto; border:0; border-radius:0; background:transparent; box-shadow:none;
  border-left:1px solid var(--border-subtle);
  padding:12px 34px 12px 14px;
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-secondary);
}
.libcontrols .field:hover{ color:var(--text-primary); background:color-mix(in srgb,var(--text-primary) 5%,transparent); }
.libcount{
  flex:0 0 auto; display:flex; align-items:center; margin:0;
  padding:0 16px; border-left:1px solid var(--border-subtle);
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); white-space:nowrap;
}

.view-toggle{
  border:1px solid var(--border-default); border-radius:var(--radius-md);
  overflow:hidden; box-shadow:var(--edge-highlight);
  background:color-mix(in srgb,var(--color-bg-deep) 70%,transparent);
}
.view-toggle button{
  border:0; border-radius:0; background:transparent; box-shadow:none;
  width:32px; height:30px; color:var(--text-faint); font-size:var(--text-xs);
}
.view-toggle button + button{ border-left:1px solid var(--border-subtle); }
.view-toggle button:hover{ color:var(--text-primary); background:color-mix(in srgb,var(--text-primary) 7%,transparent); }
.view-toggle button[aria-pressed="true"]{
  color:var(--brass-ink); background:var(--brass); box-shadow:var(--brass-edge);
}

/* the grid itself: a little more air, and a caption block that can breathe */
.libgrid{ column-gap:clamp(14px,1.5vw,24px); row-gap:clamp(26px,2.6vw,40px); }
.libstatus{
  margin:var(--sp-8) 0 0; text-align:center;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}
.libsentinel{ height:1px; }
.libback{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted); text-decoration:none;
}
.libback:hover{ color:var(--safelight-text); }


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D24 — MENUS, SWITCHES AND THE DRAWER                                     ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   The appearance popover was the worst-lit surface on the site: it sat on
   `--color-bg-elevated`, which is the greenest value in the ramp, so a small
   green rectangle hung off the top bar over an ink-blue page. Elevated is a
   good colour for a card sitting *in* the page and a bad one for a panel
   floating *over* it — over the page a panel wants to be darker than what is
   behind it, not lighter. Menus move to the deep value with a lit top edge. */

.menu, .ctx-menu, .hmenu .menu{
  background:color-mix(in srgb,var(--color-bg-deep) 97%,transparent);
  border:1px solid var(--border-default);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-xl), var(--edge-highlight), 0 0 0 1px rgba(0,0,0,.5);
  padding:var(--sp-2);
  backdrop-filter:none; -webkit-backdrop-filter:none;
}
.appearance-menu{ width:min(320px,86vw); padding:var(--sp-3); position:relative; }
.appearance-menu::before{
  content:""; position:absolute; left:var(--radius-lg); right:var(--radius-lg); top:0; height:1px;
  pointer-events:none;
  background:linear-gradient(90deg, transparent 0,
    color-mix(in srgb,var(--accent-base) 58%,transparent) 14%,
    color-mix(in srgb,var(--accent-base) 58%,transparent) 38%,
    color-mix(in srgb,var(--mf-gelatin) 20%,transparent) 66%, transparent 100%);
}
.menu-label{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); padding:var(--sp-2) var(--sp-2) var(--sp-3);
}
.menu-item, .ctx-item{
  border-radius:var(--radius-sm); padding:9px 11px;
  font-size:var(--text-sm); color:var(--text-secondary);
}
.menu-item:hover, .ctx-item:hover{
  background:color-mix(in srgb,var(--text-primary) 8%,transparent);
  color:var(--text-primary);
}

/* ── the theme picker ──────────────────────────────────────────────────── */
.themerow{
  display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-2);
  margin-bottom:var(--sp-4);
}
.themerow button{
  display:flex; align-items:center; gap:10px;
  width:100%; padding:9px 11px; text-align:left; cursor:pointer;
  border-radius:var(--radius-md);
  border:1px solid var(--border-subtle);
  background:color-mix(in srgb,var(--text-primary) 4%,transparent);
  box-shadow:none; color:var(--text-secondary);
  transition:border-color var(--dur-fast) var(--ease-standard),
             background-color var(--dur-fast) var(--ease-standard),
             color var(--dur-fast) var(--ease-standard);
}
.themerow button:hover{
  border-color:var(--border-default); color:var(--text-primary);
  background:color-mix(in srgb,var(--text-primary) 8%,transparent);
}
.themerow button[aria-pressed="true"]{
  color:var(--text-primary);
  border-color:color-mix(in srgb,var(--accent-base) 55%,transparent);
  background:color-mix(in srgb,var(--accent-base) 12%,transparent);
}
.tp-sw{
  flex:none; width:20px; height:20px; border-radius:var(--radius-xs);
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.45), var(--edge-highlight);
}
.tp-name{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}

/* ── the preference row and its switch ─────────────────────────────────── */
.pref-section{ border-top:1px solid var(--border-subtle); padding-top:var(--sp-2); }
.pref-row{
  display:flex; align-items:flex-start; gap:var(--sp-4);
  padding:var(--sp-2); border-radius:var(--radius-md); cursor:pointer;
}
.pref-row:hover{ background:color-mix(in srgb,var(--text-primary) 5%,transparent); }
.pref-title{
  display:block; font-size:var(--text-sm); color:var(--text-primary);
  margin-bottom:4px; font-weight:var(--fw-regular);
}
.pref-note{
  display:block; font-size:var(--text-xs); line-height:1.5; color:var(--text-muted);
}
.pref-switch{ position:relative; flex:none; width:38px; height:22px; margin-top:2px; }
.pref-switch input{
  position:absolute; inset:0; width:100%; height:100%; opacity:0; margin:0; cursor:pointer; z-index:2;
}
.pref-switch-track{
  position:absolute; inset:0; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--text-primary) 12%,transparent);
  border:1px solid var(--border-default);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.4);
  transition:background-color var(--dur-base) var(--ease-standard),
             border-color var(--dur-base) var(--ease-standard);
}
.pref-switch-track::after{
  content:""; position:absolute; top:2px; left:2px; width:16px; height:16px;
  border-radius:50%; background:var(--text-muted);
  box-shadow:0 1px 3px rgba(0,0,0,.5);
  transition:transform var(--dur-base) var(--ease-spring),
             background-color var(--dur-base) var(--ease-standard);
}
.pref-switch input:checked ~ .pref-switch-track{
  background:var(--brass); border-color:transparent;
  box-shadow:var(--brass-edge), inset 0 1px 2px rgba(0,0,0,.25);
}
.pref-switch input:checked ~ .pref-switch-track::after{
  transform:translateX(16px); background:var(--brass-ink);
}
.pref-switch input:focus-visible ~ .pref-switch-track{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--accent-base) 38%,transparent);
}

/* ── the notification drawer ───────────────────────────────────────────── */
.notif-backdrop{
  background:radial-gradient(ellipse 70% 55% at 50% 44%,
    color-mix(in srgb,var(--color-bg-deepest) 62%,transparent) 0%,
    rgba(1,6,9,.86) 100%);
}
.notif-panel[data-notif-panel]{
  width:380px;
  background:var(--color-bg-surface);
  border-left:1px solid var(--border-default);
  box-shadow:-24px 0 60px -24px rgba(0,0,0,.8), var(--edge-highlight);
}
.notif-panel::before{
  content:""; position:absolute; left:0; right:0; top:0; height:1px; z-index:3;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--accent-base) 60%,transparent) 0,
    var(--border-default) 42%, transparent 92%);
}
.notif-panel-header{
  padding:var(--sp-6) var(--sp-5) var(--sp-4);
  border-bottom:1px solid var(--border-subtle);
}
.notif-panel-header h2{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:var(--text-xl); letter-spacing:-.01em; color:var(--text-primary);
}
.notif-panel-header .sheet-close{ position:static; }
.notif-empty, .notif-panel > p{
  padding:var(--sp-12) var(--sp-6); margin:0; text-align:center;
  font-size:var(--text-sm); line-height:1.65; color:var(--text-muted);
}
.notif-empty::before, .notif-panel > p:first-of-type::before{
  content:""; display:block; width:38px; height:38px; margin:0 auto var(--sp-4);
  background:var(--text-faint); opacity:.45;
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
          mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
}
.notif-item{
  padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--border-subtle);
  transition:background-color var(--dur-fast) var(--ease-standard);
}
.notif-item:hover{ background:color-mix(in srgb,var(--text-primary) 4%,transparent); }
.notif-item-t{
  font-size:var(--text-sm); color:var(--text-primary); line-height:1.4;
  position:relative; padding-left:16px;
}
.notif-item-t::before{
  content:""; position:absolute; left:0; top:.5em; width:5px; height:5px;
  border-radius:50%; background:var(--accent-base);
}
.notif-item-b{
  font-size:var(--text-xs); line-height:1.55; color:var(--text-muted);
  margin-top:5px; padding-left:16px;
}

/* ── a select still needs a caret ──────────────────────────────────────── */
.libcontrols .field, select.field{
  appearance:none; -webkit-appearance:none;
  background-image:
    linear-gradient(45deg,transparent 50%,currentColor 50%),
    linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-size:5px 5px, 5px 5px;
  background-position:right 16px top 50%, right 11px top 50%;
  background-repeat:no-repeat;
}
.libcontrols .searchfield{ flex:1 1 0%; width:auto; max-width:none; }
.applink{
  margin:0; font-size:var(--text-xs); color:var(--text-muted);
}
.applink a{ color:var(--text-secondary); }
.applink a:hover{ color:var(--safelight-text); }


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D25 — THE INSTRUMENT                                                     ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   This is the surface the whole direction was named for: every technical mark
   on the site is supposed to carry live data, and this is the panel that is
   nothing but live data. It was laid out as three unrelated blocks — a big
   number with three counts floating somewhere to its right, a status board
   whose state labels sat wherever the service name happened to end, and a
   storage bar with no scale.

   The fix is columns. A readout is only legible when the same quantity lands
   in the same place on every row; that is the entire difference between a
   gauge and a list of facts.                                                 */

.instrumentbox{
  max-width:min(880px,94vw); max-height:min(880px,90vh);
  display:flex; flex-direction:column;
}
.instrument-title{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(23px,2.4vw,32px); line-height:1.1; letter-spacing:-.012em;
  color:var(--text-primary);
  margin:0 44px 6px 0; padding:clamp(24px,2.8vw,34px) clamp(24px,3vw,38px) 0;
}
.instrument-sub{
  margin:0 0 var(--sp-6); padding:0 clamp(24px,3vw,38px);
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}
.instrument-scroll{
  padding:0 clamp(24px,3vw,38px) clamp(28px,3.2vw,40px);
  overflow:auto; min-height:0;
}

/* ── the ledger ────────────────────────────────────────────────────────── */
.ledger{
  display:grid; grid-template-columns:auto 1fr; align-items:end;
  gap:var(--sp-5) clamp(24px,3.4vw,52px);
  padding-bottom:var(--sp-6);
  border-bottom:1px solid var(--border-subtle);
}
.ledger .cap{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin:0 0 var(--sp-2);
}
.ledger .big{ display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
.ledger .big #total{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(40px,5.4vw,70px); line-height:.92; letter-spacing:-.025em;
  color:var(--text-primary); font-variant-numeric:tabular-nums;
}
.ledger .big .u{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted);
}
/* the growth trace, given a baseline so a flat month reads as flat rather
   than as a stray mark */
.spark{
  display:block; width:100%; max-width:250px; height:38px;
  margin-top:var(--sp-4); overflow:visible;
  border-bottom:1px solid var(--border-subtle);
  padding-bottom:2px;
}
.spark #sparkpath{ stroke:var(--accent-base); stroke-width:1.6; }
.spark #sparkdot{ fill:var(--safelight-text); }

.stats{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(96px,1fr));
  gap:var(--sp-3) clamp(16px,2vw,30px);
  align-self:end;
}
.stat{ border-left:1px solid var(--border-subtle); padding-left:var(--sp-4); }
.stat .n{
  font-family:var(--font-mono); font-size:var(--text-xl);
  letter-spacing:var(--track-data); color:var(--text-primary);
  font-variant-numeric:tabular-nums; line-height:1.1;
}
.stat .n.zero{ color:var(--text-faint); }
.stat .l{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin-top:5px;
}

/* ── watch time by genre ───────────────────────────────────────────────── */
/* The section is never literally empty — it always holds its own caption —
   so `:empty` could not hide it and a heading sat over a void whenever the
   by-genre endpoint had nothing to say. `:has()` asks the real question. */
.genre{ padding:var(--sp-6) 0; border-bottom:1px solid var(--border-subtle); }
.genre:not(.on){ display:none; }

/* watch time by genre — label, bar, value, on one baseline */
#genrelist{ display:flex; flex-direction:column; gap:var(--sp-3); }
.grow{
  display:grid; grid-template-columns:minmax(80px,132px) minmax(0,1fr) 62px;
  align-items:center; gap:var(--sp-4);
}
.grow .gl{
  font-size:var(--text-sm); color:var(--text-secondary);
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.grow .gt{
  height:6px; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--color-bg-deepest) 62%,transparent);
  border:1px solid var(--border-subtle); overflow:hidden;
}
.grow .gt i{
  display:block; height:100%; border-radius:var(--radius-pill);
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--mf-lighttable) 62%,transparent),
    var(--lighttable-text));
}
.grow .gv{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:var(--text-muted);
  text-align:right; font-variant-numeric:tabular-nums;
}
.genre .cap, .uptime .cap{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin:0 0 var(--sp-4);
}

/* ── the status board ──────────────────────────────────────────────────── */
.uptime{ padding:var(--sp-5) 0 0; }
.uptop{ display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4); }
.board{
  border:1px solid var(--border-subtle); border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-bg-deepest) 45%,transparent);
  box-shadow:var(--edge-highlight);
  overflow:hidden;
}
/* One grid, three columns, the same on every row: who, what state, and the
   thirty-day trace. The state used to sit immediately after the service name,
   so it landed in a different place on each line and could not be scanned. */
.srow{
  display:grid;
  grid-template-columns:232px minmax(0,1fr) 54px;
  align-items:center; gap:var(--sp-4);
  padding:11px clamp(12px,1.4vw,18px);
}
.srow + .srow{ border-top:1px solid var(--border-subtle); }
.sname{
  display:flex; align-items:center; gap:9px; min-width:0; overflow:hidden;
  font-size:var(--text-sm); color:var(--text-primary); white-space:nowrap;
}
.sname .dot{
  flex:none; width:6px; height:6px; border-radius:50%;
  background:var(--status-success);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--status-success) 22%,transparent);
}
.sname .dot.down{
  background:var(--status-danger);
  box-shadow:0 0 0 2px color-mix(in srgb,var(--status-danger) 22%,transparent);
}
.sname .sstate, .sstate{
  margin-left:auto; flex:none;
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}
.sname .dot.down ~ .sstate{ color:var(--safelight-text); }
/* The thirty-day trace. Cells were 100%-height slabs with no gap, which read
   as one grey bar; they are separated ticks now, and a day with no reading is
   visibly *absent* rather than merely pale. */
.sbars{ display:flex; gap:2px; align-items:stretch; min-width:0; height:22px; }
.sbars > i{
  flex:1 1 0; min-width:2px; border-radius:1px;
  background:color-mix(in srgb,var(--text-primary) 7%,transparent);
  transition:background-color var(--dur-fast) var(--ease-standard);
}
.sbars > i.up{ background:color-mix(in srgb,var(--status-success) 62%,transparent); }
.sbars > i.down{ background:var(--status-danger); }
.sbars > i.part{ background:color-mix(in srgb,var(--status-warning) 70%,transparent); }
.sbars > i:hover{ background:color-mix(in srgb,var(--text-primary) 26%,transparent); }
.spct{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:var(--text-muted);
  text-align:right; font-variant-numeric:tabular-nums;
}
.upaxis{
  display:flex; justify-content:space-between; gap:var(--sp-4);
  margin-top:var(--sp-3);
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}

/* ── host spec + storage ───────────────────────────────────────────────── */
.chart{
  margin-top:var(--sp-6); padding-top:var(--sp-6);
  border-top:1px solid var(--border-subtle);
  display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:var(--sp-3) clamp(20px,2.6vw,40px);
}
.crow{ display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-3); }
.crow .ck{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase; color:var(--text-faint);
}
.crow .cv{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:var(--text-secondary);
  text-align:right;
}
.storage{ margin-top:var(--sp-6); }
.storage .top{
  display:flex; align-items:baseline; justify-content:space-between;
  gap:var(--sp-4); margin-bottom:var(--sp-3);
}
.storage .ck{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase; color:var(--text-faint);
}
.storage .sfree{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:var(--text-secondary);
}
/* A meter, not a line: it has a gauge behind it and a scale under it, so the
   fill means something without reading the number twice. */
.sbar{
  position:relative; height:8px; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--color-bg-deepest) 70%,transparent);
  border:1px solid var(--border-subtle);
  box-shadow:inset 0 1px 2px rgba(0,0,0,.45);
  overflow:hidden;
}
.sbar > i{
  display:block; height:100%; border-radius:var(--radius-pill);
  background:linear-gradient(90deg,#8a6a4e 0%,#c68b5a 68%,#dda875 100%);
  box-shadow:0 0 14px -4px rgba(217,160,109,.65);
  transition:width var(--dur-slower) var(--ease-standard);
}
.sbar::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:repeating-linear-gradient(90deg,
    transparent 0 calc(10% - 1px),
    color-mix(in srgb,var(--color-bg-deepest) 65%,transparent) calc(10% - 1px) 10%);
}
.roadmap{
  margin-top:var(--sp-5); font-size:var(--text-xs); line-height:1.6;
  color:var(--text-muted);
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D26 — THE THREE OUTBUILDINGS                                             ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   /wall, /journey and /request are separate documents. They carry their own
   inline component CSS and do not load app.css, so they had only ever been
   reached through tokens — and their inline rules still name tokens from two
   systems ago (`--serif`, `--raised`, `--r-lg`, `--t-lead`), several of which
   no longer resolve. The visible result was three pages that opened on a
   title, one dashed box, and eight hundred pixels of nothing: no mark, no way
   back, no bottom.

   Everything here is scoped to `body.mf-page` because these class names —
   `.card`, `.grid`, `.poster`, `.empty` — are also hub class names, and this
   stylesheet is loaded by both.                                              */

.mf-page{ min-height:100vh; display:flex; flex-direction:column; }
.mf-page .wrap{ flex:1; display:flex; flex-direction:column; width:100%; }

/* ── the page mark and the way back ────────────────────────────────────── */
.mf-pagenav{
  display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-4); flex-wrap:wrap;
  padding-bottom:var(--sp-5); margin-bottom:var(--sp-8);
  border-bottom:1px solid var(--border-subtle);
}
.mf-pagemark{
  display:inline-flex; align-items:center; gap:10px;
  text-decoration:none; color:var(--text-primary);
  font-family:var(--font-serif); font-size:var(--text-md); letter-spacing:-.005em;
}
.mf-pagemark img{ display:block; opacity:.9; }
.mf-pagemark i{
  font-style:normal; color:var(--border-strong); margin:0 5px; font-weight:var(--fw-regular);
}
.mf-pagemark:hover{ color:var(--safelight-text); }
.mf-pageback{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted); text-decoration:none;
}
.mf-pageback:hover{ color:var(--safelight-text); }

/* ── the page head ─────────────────────────────────────────────────────── */
.mf-page header.top{ margin-bottom:var(--sp-8); align-items:flex-end; }
.mf-page header.top .eyebrow{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin:0 0 var(--sp-2);
}
.mf-page h1{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(34px,5.4vw,62px); line-height:1; letter-spacing:-.02em;
  color:var(--text-primary); text-transform:none; margin:0;
}

/* ── the empty state stops being a dashed box ──────────────────────────── */
.mf-page .empty{
  border:1px solid var(--border-subtle); border-style:solid;
  border-radius:var(--radius-xl);
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--color-bg-surface) 82%,transparent),
    color-mix(in srgb,var(--color-bg-deep) 70%,transparent));
  box-shadow:var(--shadow-sm), var(--edge-highlight);
  padding:clamp(48px,7vw,86px) clamp(24px,5vw,56px);
  text-align:center; color:var(--text-muted);
}
.mf-page .empty::before{
  content:""; display:block; width:44px; height:44px; margin:0 auto var(--sp-5);
  background:var(--accent-base); opacity:.42;
  -webkit-mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
          mask:url("/static/brand/aperture-micro.svg") center/contain no-repeat;
}
.mf-page .empty h2{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:clamp(20px,2.4vw,27px); line-height:1.2; letter-spacing:-.01em;
  color:var(--text-primary); margin:0 0 var(--sp-3);
}
.mf-page .empty p{
  margin:0 auto; max-width:46ch;
  font-size:var(--text-sm); line-height:1.65; color:var(--text-muted);
}
.mf-page .empty a{ color:var(--safelight-text); text-decoration:none; border-bottom:1px solid currentColor; }
.mf-page .empty a:hover{ color:var(--text-primary); }

/* ── cards get the hub's material ──────────────────────────────────────── */
.mf-page .card{
  background:linear-gradient(180deg,
    var(--color-bg-surface), color-mix(in srgb,var(--color-bg-deep) 88%,transparent));
  border:1px solid var(--border-subtle);
  border-radius:var(--radius-lg);
  box-shadow:var(--shadow-sm), var(--edge-highlight);
  padding:var(--sp-4);
  transition:border-color var(--dur-fast) var(--ease-standard),
             box-shadow var(--dur-base) var(--ease-standard),
             transform var(--dur-base) var(--ease-standard);
}
@media(hover:hover){
  .mf-page .card:hover{
    border-color:var(--border-default);
    box-shadow:var(--shadow-md), var(--edge-highlight-strong);
    transform:translateY(-2px);
  }
}
.mf-page .poster{
  border-radius:var(--radius-md);
  border:1px solid var(--border-subtle);
  box-shadow:var(--shadow-sm);
  background:var(--color-bg-deep);
}
.mf-page .ttl, .mf-page .t, .mf-page .name{
  font-family:var(--font-serif); font-weight:var(--fw-regular);
  font-size:var(--text-lg); line-height:1.22; letter-spacing:-.008em;
  color:var(--text-primary);
}
.mf-page .sub, .mf-page .yr{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}
.mf-page .who{ font-size:var(--text-xs); color:var(--text-muted); }
.mf-page .who b{ color:var(--text-primary); font-weight:var(--fw-medium); }
.mf-page .badge{
  border-radius:var(--radius-xs);
  font-family:var(--font-mono); font-weight:var(--fw-regular);
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm);
  padding:4px 8px;
}
.mf-page .bar{
  height:3px; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--text-primary) 10%,transparent);
}
.mf-page .bar > i{
  border-radius:var(--radius-pill);
  background:linear-gradient(90deg,#a86f45,#dda875);
  box-shadow:none;
}
.mf-page .times{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); color:var(--text-faint);
}
.mf-page .live-dot{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted);
}
.mf-page .live-dot i{ width:6px; height:6px; border-radius:50%; }
.mf-page .toggle{
  border-radius:var(--radius-md);
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--text-primary) 7%,transparent),
    color-mix(in srgb,var(--text-primary) 3%,transparent));
  border:1px solid var(--border-default); box-shadow:var(--edge-highlight);
  color:var(--text-secondary);
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  padding:8px 13px; cursor:pointer;
}
.mf-page .toggle:hover{ color:var(--text-primary); border-color:var(--border-strong); }
.mf-page .toggle[aria-pressed="true"]{
  background:var(--brass); color:var(--brass-ink); border-color:transparent;
  box-shadow:var(--brass-shadow), var(--brass-edge);
}

/* ── the bottom of the page ────────────────────────────────────────────── */
.mf-pagefoot{ margin-top:auto; padding-top:var(--sp-12); }
.mf-pagefoot-rule{
  display:block; height:1px; margin-bottom:var(--sp-5);
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--accent-base) 46%,transparent) 0,
    var(--border-subtle) 22%, transparent 88%);
}
.mf-pagefoot p{
  margin:0; font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint);
}

/* the request page's search field, which is the whole page */
.mf-page .searchfield, .mf-page .field{
  border-radius:var(--radius-md);
  background:color-mix(in srgb,var(--color-bg-deep) 82%,transparent);
  border:1px solid var(--border-default);
  box-shadow:var(--edge-highlight), inset 0 1px 2px rgba(0,0,0,.28);
}
.mf-page .searchfield:focus-within{
  border-color:color-mix(in srgb,var(--accent-base) 55%,transparent);
  box-shadow:var(--edge-highlight), 0 0 0 3px color-mix(in srgb,var(--accent-base) 20%,transparent);
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D27 — TOUCH TARGETS                                                      ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   Two of these are mine. D20 sizes the player's transport in a
   `max-width:720px` block, which lands *after* the existing `max-width:640px`
   phone block in the cascade and quietly undid its 44px touch targets — the
   new rule won on order, not on intent. Scoped to the tablet band now, with
   the phone sizes restated after it so the order can never matter again.    */

/* Seven controls and a clock in 362 usable pixels. At 44px each they do not
   fit, and flex was solving that by shrinking them to 21px wide — a 44-tall
   button you cannot hit sideways, which is worse than an honestly smaller
   one. Fixed at 38 square (well clear of the 24px floor) and never allowed
   to shrink below it. */
@media(max-width:640px){
  .pv-btn, .pv-btn.btn{ width:38px; height:38px; flex:0 0 38px; }
  .pv-btn svg{ width:19px; height:19px; }
  #pv-playbtn, #pv-playbtn.btn{ width:44px; height:44px; flex:0 0 44px; }
  #pv-playbtn svg{ width:21px; height:21px; }
  .pv-close, .pv-backep{ width:44px; height:44px; }
  .pv-row{ gap:4px; }
  .pv-time{ font-size:10.5px; padding-left:3px; }
  .pv-track{ height:34px; }
  .pv-rail{ height:4px; }
  .pv-knob{ transform:translate(-50%,-50%) scale(1); }
  .pv-controls{ padding-bottom:max(16px, env(safe-area-inset-bottom)); }
}
.pv-btn, .pv-btn.btn{ flex:0 0 auto; }

/* The switch reads as a 38×22 control but its hit area is the input on top of
   it, which was exactly 38×22. Widened past the visual box on every side —
   the row is a <label> so the copy is tappable too, but the switch itself
   should not be the smallest thing on the panel. */
.pref-switch input{ inset:-11px -4px; width:auto; height:auto; }
.themerow button{ min-height:40px; }
.libback{ display:inline-block; padding:6px 2px; }
.railhead .reqline, .reqline{ padding:6px 0; }
.applink a{ display:inline-block; padding:4px 0; }


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D28 — WHAT IS PLAYING RIGHT NOW                                          ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   The one card on the site that is pure live telemetry, and its progress bar
   was a one-pixel line running from the title to the card edge — which is the
   shape of a divider, not a meter. Twice I read it as a rule between the
   title and the times. It is a gauge now, the same one the storage readout
   uses, and the two clocks sit at opposite ends of it so the gap between them
   *is* the remaining time.                                                   */

.npc{ padding:clamp(14px,1.5vw,20px); }
.np-poster{
  border-radius:var(--radius-md);
  border:1px solid var(--border-subtle);
  box-shadow:var(--shadow-sm);
  overflow:hidden;
}
.npc .row{ margin-bottom:var(--sp-2); }
.npc .who{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-muted);
}
.npc .badge{
  font-family:var(--font-mono); font-weight:var(--fw-regular);
  font-size:var(--text-3xs); letter-spacing:var(--track-caps-sm);
  color:var(--text-muted); background:transparent;
  border:1px solid var(--border-default); border-radius:var(--radius-xs);
  padding:3px 7px;
}
.npc .ttl{
  font-size:var(--text-lg); line-height:1.28; color:var(--text-primary);
  margin-bottom:var(--sp-2);
}
.npc .ttl .se{
  font-family:var(--font-mono); font-size:var(--text-2xs);
  letter-spacing:var(--track-data); color:var(--safelight-text);
  margin:0 2px;
}
.npc .ttl .tag{ color:var(--text-faint); margin:0 5px; }
.npc .meta{
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  color:var(--text-faint); margin-bottom:var(--sp-4);
}
.npc.paused .meta{ color:var(--safelight-text); }

.npc .bar{
  height:6px; border-radius:var(--radius-pill);
  background:color-mix(in srgb,var(--text-primary) 11%,transparent);
  border:0;
  box-shadow:inset 0 1px 2px rgba(0,0,0,.35);
  overflow:hidden;
}
.npc .bar i{
  display:block; height:100%; border-radius:var(--radius-pill);
  background:linear-gradient(90deg,#a86f45,#dda875);
  box-shadow:0 0 12px -4px rgba(217,160,109,.7);
  transition:width var(--dur-slower) var(--ease-standard);
}
.npc.paused .bar i{ background:linear-gradient(90deg,#5c6b66,#8a9892); box-shadow:none; }

.npc .np-row{ margin-top:var(--sp-3); align-items:center; gap:var(--sp-4); }
.npc .time{
  display:flex; justify-content:space-between; flex:1; gap:var(--sp-4);
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-data); color:var(--text-faint);
  font-variant-numeric:tabular-nums;
}
.npc .np-join{
  border-radius:var(--radius-md);
  font-family:var(--font-mono); font-size:var(--text-3xs);
  letter-spacing:var(--track-caps-sm); text-transform:uppercase;
  padding:7px 11px;
}


/* ── D29 — Adult Swim reconciliation ──────────────────────────────────────
   Everything above is written against tokens, so the second theme inherits
   the structure and overrides the material through `tokens.css` — square
   corners, flat fills, its own type. Three places needed a word anyway,
   because D20–D26 set a literal rather than a token and a literal cannot be
   re-themed. */
html[data-theme="adultswim"] .pv-box{
  border-color:rgba(255,255,255,.16);
  box-shadow:0 0 0 1px rgba(0,0,0,.9), 0 24px 60px -20px rgba(0,0,0,.9);
}
/* the safelight rule is a Darkroom signature; Adult Swim's panels are edged
   in white, and a warm hairline across the top just reads as a mistake */
html[data-theme="adultswim"] .sheet::before,
html[data-theme="adultswim"] .palette::before,
html[data-theme="adultswim"] .appearance-menu::before,
html[data-theme="adultswim"] .notif-panel::before{
  background:linear-gradient(90deg,
    var(--aurora-amber) 0 64px, rgba(255,255,255,.22) 64px 46%, transparent 92%);
}
/* the player's transport is brass in Darkroom and signage orange here */
html[data-theme="adultswim"] .pv-played{
  background:var(--aurora-amber); box-shadow:none;
}
html[data-theme="adultswim"] .pv-knob{
  background:#fff; box-shadow:0 0 0 2px #000;
}


/* ╔═══════════════════════════════════════════════════════════════════════════╗
   ║  D30 — THE LAST FILTER, AND THE SCROLL                                    ║
   ╚═══════════════════════════════════════════════════════════════════════════╝

   Two things Pass 4 shipped with a note attached instead of a fix.

   ── 1. The last filter ──────────────────────────────────────────────────────
   `.lb-art--poster` is the header wash for a title with no backdrop on file:
   the poster, scaled up, under `filter:blur(22px) saturate(1.15)`. It was
   fetched at h=720 — a full-size image downloaded so a shader could destroy
   the detail again, re-blurring a ~1000×400 region for as long as the modal
   stayed open.

   A blur is just a loss of high-frequency detail, and there is a free way to
   lose it: ask the server for the image at 40 pixels tall and let the
   browser's own bilinear upscale do the smoothing. Same soft field, about a
   kilobyte on the wire, no filter, and nothing for the compositor to redo.
   renderFilmDetail() now requests h=40 on this path only — a real backdrop
   still comes at full height. */
.lb-art--poster{
  filter:none;
  transform:scale(1.06);
  background-position:center 34%;
  /* the upscale is the blur; asking for it explicitly stops a future
     `image-rendering:pixelated` reset from turning this into a mosaic */
  image-rendering:auto;
}
/* saturate(1.15) went with the blur. A 40px source is already averaged toward
   grey, so it gets its warmth back the free way — a wash in the accent over
   the top of it rather than a colour-matrix pass over every pixel. */
.lb-art--poster::after{
  content:""; position:absolute; inset:0;
  background:linear-gradient(150deg,
    color-mix(in srgb,var(--accent-base) 12%,transparent),
    transparent 55%,
    color-mix(in srgb,var(--mf-lighttable) 9%,transparent));
  mix-blend-mode:normal;
}

/* ── 2. The ground ─────────────────────────────────────────────────────────
   `body { background-attachment: fixed }` was introduced in Pass 3 with a
   comment claiming it "costs one paint". That is exactly wrong, and it is the
   one plausible mechanical cause of the scroll jank that four benchmark runs
   could not pin down in a container.

   A fixed background attachment is not composited: because the background has
   to stay put while the content moves, the browser cannot simply translate the
   scrolling layer — it has to repaint the viewport-sized gradient against the
   new scroll offset on every frame. On a 2560-wide window that is a full-width
   repaint per frame for a decoration nobody would miss for a millisecond.

   The gradient moves to a fixed pseudo-element on the root instead. A
   fixed-position layer is promoted and composited once; scrolling translates
   the content above it and never touches it again. Pixel-identical, and it
   costs one paint for real this time. */
html{ background-color:var(--color-bg-deepest); }
html::before{
  content:""; position:fixed; inset:0; z-index:-1; pointer-events:none;
  background-image:var(--ground-gradient);
}
/* body's own fill has to go transparent as well as its image: an in-flow
   block background paints in step 4 of the stacking order and a negative
   z-index child paints in step 3, so an opaque body would sit on top of the
   layer and hide it. The colour moves to the root, where it becomes the
   canvas — which is what was actually happening before, one element down. */
body{ background-color:transparent; background-image:none; background-attachment:scroll; }
@media print{
  html{ background:#fff; }
  html::before{ display:none !important; }
}
