/* ==========================================================================
   NITRATE — THE SCREENER
   Consumes tokens.css.  Never redefines a token.

   Layout: the screener owns exactly one viewport-height block.  The page
   itself never scrolls vertically; the table body does.  That is what makes
   a sticky header row and a sticky first column actually work.
   ========================================================================== */

.scr {
  --rowh: 48px;
  --headh: 34px;
  --railw: 244px;
  --sheetw: min(560px, 100vw);
  --scr-top: 96px;              /* measured from .masthead by screener.js */

  position: relative;
  display: flex;
  flex-direction: column;
  /* The shell publishes --shell-tabbar: how much of the bottom of the screen
     it has taken for the phone destination bar (0px everywhere else, always
     defined).  The Screener is the one view that is not a scrolling page — it
     sizes ONE block to the viewport and scrolls the table inside it — so
     bottom padding would only add empty document.  It has to come out of the
     height, or the last row of the table sits under the bar. */
  height: calc(100vh - var(--scr-top) - var(--shell-tabbar));
  height: calc(100dvh - var(--scr-top) - var(--shell-tabbar));
  overflow: hidden;
  background: var(--ground);
  margin-right: 0;
  transition: margin-right .16s ease;
}
.scr[hidden] { display: none; }

.scr[data-density="condensed"] { --rowh: 40px; }
.scr[data-density="regular"]   { --rowh: 48px; }
.scr[data-density="relaxed"]   { --rowh: 56px; }

/* the detail sheet pushes the table narrower — neighbouring rows stay visible */
.scr.sheet-open { margin-right: var(--sheetw); }

.scr-body { flex: 1 1 auto; min-height: 0; display: flex; }
.scr-spacer { flex: 1 1 auto; }

/* ============================================================ RAIL / FACETS */
.scr-rail {
  flex: 0 0 var(--railw);
  width: var(--railw);
  min-width: 0;
  border-right: var(--hair) solid var(--rule);
  background: var(--panel);
  overflow: hidden;
}
.scr.rail-off .scr-rail { flex-basis: 0; width: 0; border-right: 0; }
.scr-rail-inner { height: 100%; overflow-y: auto; overscroll-behavior: contain; }

.scr-sec + .scr-sec { border-top: var(--hair) solid var(--rule); }

.scr-sec-h {
  display: flex; align-items: center; gap: 7px;
  width: 100%; padding: 7px 10px 6px;
  border: 0; background: transparent; text-align: left;
}
.scr-sec-h:hover { background: var(--panel-2); }
.scr-sec-caret { color: var(--muted); font-size: 9px; width: 8px; }
.scr-sec-l {
  font-family: var(--font-display); font-weight: 800; font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-2);
}
.scr-sec-badge {
  margin-left: auto;
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
  min-width: 12px; text-align: right;
}
.scr-sec-badge.on { color: var(--accent); }

.scr-sec-b { padding: 2px 10px 9px; }

.scr-opts { display: block; }
.scr-opts-tall { max-height: 232px; overflow-y: auto; overscroll-behavior: contain; }

.scr-opt {
  display: flex; align-items: center; gap: 7px;
  padding: 2px 0; cursor: pointer; min-height: 20px;
}
.scr-opt:hover { outline: var(--hair) solid var(--rule); outline-offset: 0; }
.scr-opt input { flex: 0 0 auto; margin: 0; accent-color: var(--accent); }
.scr-opt-n {
  flex: 1 1 auto; min-width: 0;
  font-size: 12px; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scr-opt-c {
  flex: 0 0 auto;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 10.5px; color: var(--muted);
}
.scr-opt.is-zero { opacity: .38; }
.scr-opt.is-zero:hover { opacity: .7; }
.scr-opt-plain { padding-top: 5px; }

.scr-optsearch,
.scr-select,
.scr-num,
.scr-search input {
  width: 100%;
  padding: 3px 6px;
  background: var(--ground);
  border: var(--hair) solid var(--rule);
  color: var(--ink);
  font-size: 12px;
}
.scr-num, .scr-optsearch { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.scr-optsearch { margin-bottom: 5px; }
.scr-select { margin-bottom: 5px; }

.scr-range { display: flex; align-items: center; gap: 6px; }
.scr-range .scr-num { text-align: right; }
.scr-range-sep { color: var(--muted); font-size: 11px; }

.scr-seg { display: flex; }
.scr-segbtn {
  flex: 1 1 0; min-width: 0;
  padding: 3px 4px;
  font-family: var(--font-mono); font-size: 10.5px;
  color: var(--muted);
  border: var(--hair) solid var(--rule);
  background: var(--ground);
}
.scr-segbtn + .scr-segbtn { border-left: 0; }
.scr-segbtn:hover { color: var(--ink); }
/* "any" is the no-filter default: selected, but nothing to flag */
.scr-segbtn[aria-pressed="true"] { color: var(--ink); background: var(--panel-2); }
.scr-segbtn[aria-pressed="true"]:not([data-default]) {
  color: var(--accent-ink); background: var(--accent); border-color: var(--accent);
}

/* ================================================================= TOOLBAR */
.scr-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.scr-toolbar {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 6px;
  padding: 6px 10px;
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}

.scr-tbbtn {
  padding: 4px 9px 3px;
  font-family: var(--font-display); font-weight: 800; font-size: 10px;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-2);
  border: var(--hair) solid var(--rule);
  background: var(--ground);
  white-space: nowrap;
}
.scr-tbbtn:hover { color: var(--ink); background: var(--panel-2); }
/* a toggled-open panel is a preference, not an alert — orange is reserved for
   the active sort and for filters that are hiding rows from you */
.scr-tbbtn[aria-pressed="true"], .scr-tbbtn.on {
  color: var(--ink); background: var(--panel-2); border-color: var(--rule);
}
.scr-tbbtn:disabled { opacity: .45; cursor: default; }

.scr-search { flex: 0 1 300px; min-width: 130px; }
.scr-search input { padding: 4px 7px; }
.scr-search input::placeholder { color: var(--muted); }

.scr-fcount {
  padding: 4px 9px 3px;
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted);
  border: var(--hair) solid transparent;
  background: transparent;
  white-space: nowrap;
}
.scr-fcount.on { color: var(--accent); border-color: var(--accent); }
.scr-fcount.on:hover { background: var(--accent); color: var(--accent-ink); }
.scr-fcount:disabled { cursor: default; }

.scr-dens { display: flex; }
.scr-densbtn {
  width: 24px; padding: 4px 0 3px;
  font-family: var(--font-mono); font-size: 10.5px;
  color: var(--muted);
  border: var(--hair) solid var(--rule);
  background: var(--ground);
}
.scr-densbtn + .scr-densbtn { border-left: 0; }
.scr-densbtn:hover { color: var(--ink); }
.scr-densbtn[aria-pressed="true"] { color: var(--ink); background: var(--panel-2); }

/* ==================================================== ACTIVE FILTER CHIPS */
.scr-active {
  flex: 0 0 auto;
  display: flex; flex-wrap: wrap; gap: 4px;
  padding: 5px 10px;
  background: var(--ground);
  border-bottom: var(--hair) solid var(--rule);
  max-height: 78px; overflow-y: auto;
}
.scr-achip {
  display: inline-flex; align-items: stretch;
  border: var(--hair) solid var(--rule);
  background: var(--panel);
  font-size: 11px; line-height: 1.6;
}
.scr-achip-k {
  padding: 0 5px;
  font-family: var(--font-mono); font-size: 9.5px;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted);
  background: var(--panel-2);
}
.scr-achip-v {
  padding: 0 6px; color: var(--ink);
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scr-achip-x {
  padding: 0 5px; border: 0; background: transparent;
  color: var(--muted); font-size: 13px; line-height: 1.35;
}
.scr-achip-x:hover { color: var(--accent-ink); background: var(--crit); }

/* ============================================================= SQL CONSOLE */
.scr-sql {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  height: 320px; max-height: 55%;
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-sql[hidden] { display: none; }
.scr-sql-head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-sql-title { font-size: 11px; letter-spacing: .1em; color: var(--ink); }
.scr-sql-hint {
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scr-sql-in {
  flex: 0 0 84px;
  width: 100%; padding: 7px 10px;
  background: var(--ground); color: var(--ink);
  border: 0; border-bottom: var(--hair) solid var(--rule);
  font-family: var(--font-mono); font-size: 12px; line-height: 1.5;
  resize: vertical;
}
.scr-sql-out { flex: 1 1 auto; min-height: 0; overflow: auto; }
.scr-sql-msg {
  padding: 5px 10px;
  font-family: var(--font-mono); font-size: 11px; color: var(--muted);
}
.scr-sql-tablewrap { overflow-x: auto; }
/* hug the content — a 3-column result stretched across 1600px is unreadable */
.scr-sql-table { border-collapse: separate; border-spacing: 0; width: auto; min-width: 0; }
.scr-sql-table th, .scr-sql-table td {
  border: 0; box-shadow: inset 0 -1px 0 var(--rule);
  padding: 3px 10px; text-align: left; white-space: nowrap;
  font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 11.5px;
}
.scr-sql-table th {
  position: sticky; top: 0; z-index: 1;
  background: var(--panel-2); color: var(--ink);
  font-weight: 700; text-transform: uppercase; letter-spacing: .06em; font-size: 10px;
}
.scr-sql-table td { color: var(--ink-2); }
.scr-sql-table td.num { text-align: right; }

/* ============================================================= LIVE STREAMS
   The admin's "what is playing right now" panel.  It sits where the SQL
   console sits, is built the same way, and is admin-only for the same reason
   the whole Screener is: it names people and what they are watching.

   The visual order is deliberate and is not the order of the data:
     1  CAPACITY, across the top, because a refusal is a house-level fact and
        the number that caused it is the answer John actually needs.
     2  the streams, one block each, three lines: WHO/WHAT, then the DECISION,
        then WHERE THEY ARE.
   Nothing here is a live-updating animation: the panel repaints wholesale
   every 2.5 s, which is cheap at eight rows and cannot drift out of step. */
.scr-live {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  max-height: 62%;
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-live[hidden] { display: none; }
.scr-live-head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-live-title { font-size: 11px; letter-spacing: .1em; color: var(--ink); }
.scr-live-clock {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}

/* ---- capacity strip ---- */
.scr-live-caps {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
  gap: 1px;
  background: var(--rule);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-cap { background: var(--panel); padding: 7px 10px 8px; }
.scr-cap-wide { grid-column: span 2; }
.scr-cap-k {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted);
}
.scr-cap-v {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 18px; line-height: 1.25; color: var(--ink);
}
.scr-cap-v strong { font-weight: 800; }
.scr-cap-of { font-size: 11px; color: var(--muted); }
.scr-cap-bar { height: 3px; background: var(--panel-2); margin-top: 3px; }
.scr-cap-fill { height: 100%; background: var(--ink-2); }
/* 🔴 A FULL CAP IS THE WHOLE REASON THIS PANEL EXISTS — it is what a 503 or a
   429 is made of, so it is the one thing here allowed to shout. */
.scr-cap.is-full .scr-cap-v,
.scr-cap.is-full .scr-cap-k { color: var(--crit); }
.scr-cap.is-full .scr-cap-fill { background: var(--crit); }
.scr-cap-note {
  font-size: 10px; line-height: 1.35; color: var(--muted); margin-top: 4px;
}
.scr-cap-list { margin-top: 2px; }
.scr-cap-li {
  display: flex; align-items: baseline; gap: 8px;
  font-family: var(--font-mono); font-size: 11px; color: var(--ink-2);
}
.scr-cap-li.is-full { color: var(--crit); }
.scr-cap-who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.scr-cap-n { margin-left: auto; font-variant-numeric: tabular-nums; }

/* ---- one stream ---- */
.scr-live-rows { flex: 1 1 auto; min-height: 0; overflow: auto; }
.scr-live-row {
  position: relative;
  padding: 8px 100px 9px 10px;      /* room at the right for STOP… */
  border-bottom: var(--hair) solid var(--rule);
}
.scr-live-row.is-bad { background: color-mix(in srgb, var(--crit) 7%, var(--panel)); }
.scr-live-l1 {
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
}
.scr-live-who {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: .04em; color: var(--accent); text-transform: uppercase;
}
.scr-live-what { font-size: 13.5px; color: var(--ink); }
/* the screen it is being watched on, set beside the name it belongs to */
.scr-live-dev {
  padding: 1px 6px 0;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .03em;
  color: var(--ink-2); background: var(--panel-2);
  border: var(--hair) solid var(--rule);
  white-space: nowrap;
}
/* ⭐ A HARNESS IS NOT A VIEWER. curl and headless Chrome are a test or a
   script, and mistaking one for a person is exactly what happened on the day
   this panel shipped — a test stream was read as an orphaned session and
   stopped. It is marked so nobody has to guess again. */
.scr-live-dev.is-harness {
  color: var(--warn); border-color: var(--warn); font-weight: 700;
}
.scr-live-l2 { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.scr-live-tag {
  padding: 2px 6px 1px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em;
  color: var(--ink-2); background: var(--panel-2);
  border: var(--hair) solid var(--rule);
  white-space: nowrap;
}
.scr-live-tag.is-transcode { color: var(--ink); border-color: var(--ink-2); font-weight: 700; }
.scr-live-tag.is-remux { color: var(--muted); }
/* ⭐ WHERE THE ENCODE IS RUNNING IS THE HEALTH SIGNAL, so the two boxes do not
   look alike: the GPU is the plan, libx264 on the 2009 Xeon is the fallback
   and means bugeye was unreachable, full, or refused the codec. */
.scr-live-tag.is-gpu { color: var(--ok); border-color: var(--ok); }
.scr-live-tag.is-cpu { color: var(--warn); border-color: var(--warn); font-weight: 700; }
/* 🔴 burn-in measures 0.43x realtime and is why a stream stalls */
.scr-live-tag.is-burn { color: var(--crit); border-color: var(--crit); font-weight: 700; }
.scr-live-l3 {
  display: flex; flex-wrap: wrap; gap: 12px; margin-top: 5px;
  font-family: var(--font-mono); font-size: 10.5px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.scr-live-pos { color: var(--ink-2); }
.scr-live-act { position: absolute; top: 8px; right: 10px; }
.scr-live-stop:hover { color: var(--crit); border-color: var(--crit); }

/* ---- casts, and the one build that costs something ------------------------
   ⭐ A CAST ROW SITS BESIDE A STREAM ROW because it answers the same question —
   what is the house serving — and it is marked because it answers it about a
   different road: the television fetched the file and is decoding it itself.
   The left rule is the only decoration; everything else is the same vocabulary,
   which is what makes the two comparable at a glance. */
.scr-live-row.is-cast {
  padding-right: 132px;             /* "STOP ON TV…" is a wider button */
  border-left: 2px solid var(--accent);
  padding-left: 8px;
}
.scr-live-row.is-build {
  padding-right: 10px;
  border-left: 2px solid var(--warn);
  padding-left: 8px;
}
/* the television itself, named from its own UPnP description */
.scr-live-dev.is-tv { color: var(--ink); border-color: var(--ink-2); }
.scr-live-tag.is-cast { color: var(--ink); border-color: var(--ink-2); font-weight: 700; }
/* ⭐ NO ENCODER IS GOOD NEWS, so it is the one green thing on the row. It is
   measured, not asserted: John's cast ran with `pgrep ffmpeg` = 0. */
.scr-live-tag.is-free { color: var(--ok); border-color: var(--ok); font-weight: 700; }
/* ⚠️ a slim IS ffmpeg while it builds — see the capacity strip */
.scr-live-tag.is-slim { color: var(--warn); border-color: var(--warn); }
/* what cannot be done from here, said instead of a button that would not work */
.scr-live-ro {
  padding: 2px 6px 1px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em;
  color: var(--muted); border: var(--hair) dashed var(--rule);
}
.scr-cap.is-cast .scr-cap-v strong { color: var(--ok); }
.scr-cap.is-slim .scr-cap-v strong { color: var(--warn); }
.scr-live-msg {
  padding: 10px; font-family: var(--font-mono); font-size: 11px; color: var(--muted);
}
.scr-live-foot {
  flex: 0 0 auto;
  margin: 0; padding: 7px 10px 8px;
  border-top: var(--hair) solid var(--rule);
  background: var(--panel-2);
  font-size: 10.5px; line-height: 1.45; color: var(--muted);
}

/* ---- the stop dialog ---- */
.scr-modal-back {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, .55);
}
.scr-modal {
  width: min(520px, 100%);
  max-height: 90vh; overflow: auto;
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  box-shadow: 0 26px 64px -26px rgba(0, 0, 0, .8);
}
.scr-modal-k {
  padding: 9px 14px 8px;
  border-bottom: var(--hair) solid var(--rule);
  background: var(--panel-2);
  font-family: var(--font-mono); font-size: 10px; font-weight: 700;
  letter-spacing: .2em; color: var(--crit);
}
.scr-modal-h { padding: 12px 14px 2px; font-size: 15px; color: var(--ink); }
.scr-modal-p { margin: 8px 14px; font-size: 11.5px; line-height: 1.5; color: var(--muted); }
.scr-modal-lab {
  display: block; margin: 12px 14px 4px;
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-2);
}
.scr-modal-in {
  display: block; width: calc(100% - 28px); margin: 0 14px;
  padding: 8px 10px;
  background: var(--ground); color: var(--ink);
  border: var(--hair) solid var(--rule);
  font-family: inherit; font-size: 13px; line-height: 1.45;
  resize: vertical;
}
.scr-modal-count {
  margin: 3px 14px 0; text-align: right;
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
}
.scr-modal-acts {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 14px; border-top: var(--hair) solid var(--rule); margin-top: 10px;
}
.scr-modal-go { color: var(--crit); border-color: var(--crit); }
.scr-modal-out:empty { display: none; }
.scr-modal-msg {
  padding: 0 14px 12px; font-family: var(--font-mono); font-size: 11px; color: var(--ink-2);
}

@media (max-width: 760px) {
  .scr-live { max-height: 70%; }
  .scr-cap-wide { grid-column: span 1; }
  .scr-live-row { padding-right: 10px; padding-bottom: 40px; }
  /* ⚠️ .is-cast wins on specificity, so its wider right gutter has to be
     handed back here or the button would sit on top of the text. */
  .scr-live-row.is-cast { padding-right: 10px; }
  .scr-live-row.is-build { padding-bottom: 9px; }
  .scr-live-act { top: auto; bottom: 8px; right: 10px; }
}

/* ==================================================================== TABLE */
.scr-scroll {
  flex: 1 1 auto; min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  position: relative;
  background: var(--ground);
}
/* the table is keyboard-navigable; a 2px offset ring around the whole pane is
   too loud, an inset hairline says "arrow keys land here" just as clearly */
.scr-scroll:focus-visible { outline: var(--hair) solid var(--accent); outline-offset: -1px; }

/* border-collapse:separate is not cosmetic — collapsed borders do not stick,
   they scroll away from a position:sticky header.  Hairlines are box-shadows. */
.scr-table {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  table-layout: fixed;
}
.scr-table[hidden] { display: none; }

.scr-table th, .scr-table td {
  border: 0;
  padding: 0 8px;
  text-align: left;
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  box-shadow: inset 0 -1px 0 var(--rule);
}
.scr-table th.num, .scr-table td.num { text-align: right; }
.scr-table td.num, .scr-table td.mono, .scr-table th.num, .scr-table th.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ---- sticky header row ---- */
.scr-table thead th {
  position: sticky; top: 0; z-index: 3;
  height: var(--headh);
  padding: 0;
  background: var(--panel);
  box-shadow: inset 0 -1px 0 var(--rule);
}
/* ---- sticky first column ---- */
.scr-table th.scr-sticky, .scr-table td.scr-sticky {
  position: sticky; left: 0; z-index: 2;
  background: var(--ground);
  box-shadow: inset 0 -1px 0 var(--rule), 1px 0 0 var(--rule);
}
.scr-table thead th.scr-sticky {
  z-index: 4;
  background: var(--panel);
  box-shadow: inset 0 -1px 0 var(--rule), 1px 0 0 var(--rule);
}

.scr-th {
  display: flex; align-items: center; gap: 5px;
  width: 100%; height: var(--headh);
  padding: 0 8px;
  border: 0; background: transparent;
  font-family: var(--font-display); font-weight: 800; font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap; overflow: hidden;
}
.scr-table th.num .scr-th { justify-content: flex-end; }
.scr-th:hover { color: var(--ink); }
.scr-th-l { overflow: hidden; text-overflow: ellipsis; }
.scr-th-u {
  font-family: var(--font-mono); font-weight: 400; font-size: 9px;
  letter-spacing: 0; text-transform: none; color: var(--muted);
}
.scr-th-a { font-size: 8px; line-height: 1; }
.scr-table th.is-sorted .scr-th,
.scr-table th.is-sorted .scr-th-u,
.scr-table th.is-sorted .scr-th-a { color: var(--accent); }
.scr-table th.is-sorted { box-shadow: inset 0 -2px 0 var(--accent); }
.scr-table thead th.is-sorted.scr-sticky {
  box-shadow: inset 0 -2px 0 var(--accent), 1px 0 0 var(--rule);
}

/* ---- rows: hairlines, never zebra stripes ---- */
.scr-table tbody td {
  height: var(--rowh);
  color: var(--ink-2);
  font-size: var(--size-dense);
}
.scr-table tbody td.scr-sticky { color: var(--ink); }
.scr-table tbody tr.scr-row { cursor: pointer; }

.scr-table tbody tr.scr-row:hover > td { background: var(--panel); }
.scr-table tbody tr.scr-row:hover > td.scr-sticky { background: var(--panel); }
.scr-table tbody tr.is-cursor > td { background: var(--panel-2); }
.scr-table tbody tr.is-cursor > td.scr-sticky { background: var(--panel-2); }
.scr-table tbody tr.is-selected > td { background: var(--panel-2); }
.scr-table tbody tr.is-selected > td.scr-sticky {
  background: var(--panel-2);
  box-shadow: inset 3px 0 0 var(--accent), inset 0 -1px 0 var(--rule), 1px 0 0 var(--rule);
}

/* virtual-scroll spacers */
.scr-table tbody tr.scr-pad > td {
  padding: 0; box-shadow: none; background: transparent;
  position: static; height: auto;
}

.scr-t { color: inherit; }
.scr-t-sub { color: var(--muted); font-family: var(--font-mono); font-size: 10.5px; }
.scr-nil { color: var(--muted); }
.scr-kind {
  font-family: var(--font-mono); font-size: 10.5px;
  letter-spacing: .04em; color: var(--muted);
}

/* ---- chips ---- */
.scr-chip {
  display: inline-block;
  margin-right: 3px;
  padding: 1px 5px;
  border: var(--hair) solid var(--rule);
  background: transparent;
  font-family: var(--font-mono); font-size: 10px;
  line-height: 1.5;
  letter-spacing: .02em;
  color: var(--ink-2);
  max-width: 100%;
  vertical-align: middle;
}
.scr-chip:hover { color: var(--accent); border-color: var(--accent); }
.scr-chip-grp { color: var(--ink); background: var(--panel-2); border-color: transparent; }
.scr-chip-grp:hover { background: var(--panel-2); }
.scr-chip-drv { color: var(--ink-2); }
/* SINGLE_COPY alone lands on 82% of the library.  If every warn chip were
   outlined in its own colour the table would read as one solid alarm, so only
   crit carries a coloured edge; warn and info carry colour in the text. */
.scr-flag.sev-info { color: var(--muted); }
.scr-flag.sev-warn { color: var(--warn); }
.scr-flag.sev-crit { color: var(--crit); border-color: currentColor; }
.scr-flag:hover { color: var(--accent); border-color: var(--accent); }

/* ================================================================== STATUS */
.scr-status {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 14px;
  padding: 4px 10px 5px;
  background: var(--panel);
  border-top: var(--hair) solid var(--rule);
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden;
}
.scr-count { color: var(--ink); }
.scr-sortnote { color: var(--accent); }

/* ============================================================ EMPTY STATES */
.scr-empty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; padding: 40px 24px; text-align: center;
}
.scr-empty[hidden] { display: none; }
.scr-empty-h { font-size: 17px; color: var(--ink); }
.scr-empty-p { max-width: 46ch; color: var(--muted); font-size: 12.5px; margin: 0; }
.scr-clearbig { margin-top: 4px; color: var(--accent); border-color: var(--accent); }
.scr-clearbig:hover { color: var(--accent-ink); background: var(--accent); }

/* =========================================================== COLUMN PICKER */
.scr-pop {
  position: absolute; z-index: 50;
  width: 296px; max-height: 70vh;
  display: flex; flex-direction: column;
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .3);
}
.scr-pop[hidden] { display: none; }
.scr-pop-h {
  display: flex; align-items: center; gap: 6px;
  padding: 6px 8px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-pop-t { font-size: 12px; letter-spacing: .08em; }
.scr-pop-hint {
  margin: 0; padding: 6px 10px;
  font-size: 11px; color: var(--muted);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-collist { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 3px 0; }

.scr-colitem {
  display: flex; align-items: center; gap: 6px;
  padding: 2px 8px;
  min-height: 24px;
  background: var(--panel);
}
.scr-colitem:hover { outline: var(--hair) solid var(--rule); outline-offset: -1px; }
.scr-colitem.dragging { opacity: .45; outline: var(--hair) dashed var(--accent); }
.scr-colitem[draggable="true"] { cursor: grab; }
.scr-grip { color: var(--muted); font-size: 11px; width: 10px; text-align: center; }
.scr-collab { display: flex; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; cursor: pointer; }
.scr-collab input { margin: 0; accent-color: var(--accent); }
.scr-collab-t {
  font-family: var(--font-display); font-weight: 800; font-size: 10px;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scr-collab-u { font-family: var(--font-mono); font-size: 9.5px; color: var(--muted); }
.scr-colmv { display: flex; gap: 2px; }
.scr-mvbtn {
  width: 20px; padding: 1px 0;
  font-size: 8px; color: var(--muted);
  border: var(--hair) solid var(--rule); background: var(--ground);
}
.scr-mvbtn:hover { color: var(--accent); border-color: var(--accent); }

/* ================================================================ RESPONSE */
@media (max-width: 900px) {
  .scr { --railw: 210px; }
  .scr.sheet-open { margin-right: 0; }   /* no room to push — let it overlay */
}

/* ==========================================================================
   THE SCREENER IN THE HAND  (<= 760px, the MOBILE tier)

   2026-08-11, MOBILE_SPEC.md: this block used to break at 720px — one of the
   24 independently-invented breakpoints the spec exists to kill.  It is now
   760, the house mobile tier.  The band it gained, 721-760, previously got the
   DESK screener on a screen too narrow for it: a 244px facet rail pinned
   beside a table, 34px header cells and 19px chips.  Nothing above 760 moved.

   WHAT THIS STAYS.
   It stays a table.  Every other dense-table-on-a-phone answer — one card per
   title, a stack of label/value pairs — deletes the three things the Screener
   is for: comparing ONE figure down a column, choosing which columns you get,
   and sorting by any of them.  It would also delete the virtual scroller,
   which draws 2,827 rows by trusting that every row is exactly --rowh tall.
   A spreadsheet with a frozen first column and a horizontal scroll is a
   pattern every phone user already owns, and it keeps the tool intact.

   WHAT CHANGES.
     the rail    a 210px slab pinned over the left half of a 390px screen —
                 covering the title column, the one column you steer by — is
                 not a sidebar, it is an obstruction.  It becomes a full-bleed
                 drawer over the table, opened and closed by FILTERS, and it
                 opens CLOSED (screener.js).  The toolbar stays above it so
                 the button that opened it is never underneath it.
     the toolbar wraps instead of squeezing the search box to 130px, and every
                 control in it is a 44px target.
     the rows    already 48px and already the tap target that opens a title.
                 Row height is NOT touched here: ROWH in screener.js mirrors
                 --rowh and the virtual scroller measures offsets with it, so
                 a stylesheet that changed it would tear the list apart.
     the chips   the in-cell filter chips are 19px high on a desk.  They are
                 the only reason to touch a cell rather than the row, so they
                 grow into their row's full height and get real gaps.
     the width   TITLE's 300px is an inline <col> style — see titleW() in
                 screener.js, the one thing here CSS could not reach.

   🔴 THE BUG THIS BLOCK SHIPPED WITH, and the one line that fixes it.
   Turning .scr-body's flex direction to `column` below moves .scr-main from
   the CROSS axis to the MAIN axis — and a flex item's `min-height` defaults to
   `auto`, which on the main axis means "never smaller than my content".  So
   .scr-main grew to the height of all 2,889 rows: measured 138,921px inside a
   773px .scr that clips with overflow:hidden.  .scr-scroll's clientHeight then
   EQUALLED its scrollHeight, so the pane could not scroll at all — every row
   past the eleventh was unreachable on a phone, the status strip sat at
   y=138,961, and the virtual scroller (which sizes its window from that same
   clientHeight) painted all 2,889 rows instead of ~20.  `min-height: 0` on
   .scr-main is the whole fix.  The desk never saw it: in a ROW-direction
   parent, min-height:auto is a cross-axis stretch and resolves to the row's
   own height.  ⛔ .scr-main's existing `min-width: 0` is the same guard for the
   other axis — do not read one as covering both.
   ========================================================================== */
@media (max-width: 760px) {
  /* THE DRAWER IS A TAKEOVER, NOT AN OVERLAY.
     The rail and the toolbar are siblings inside .scr-body — the button that
     opens the drawer lives INSIDE the box the drawer would cover — so an
     absolutely-positioned overlay either buries its own close control or has
     to guess the toolbar's height in pixels and stay right about it forever.
     Stacking the body instead costs nothing and cannot drift: the toolbar
     keeps its natural height at the top, the rail takes every pixel below it,
     and the table simply stands down while the drawer is up.  FILTERS is
     pressed to open and pressed again to close, in the same place both times. */
  /* 🔴 THE HEIGHT AT THE TOP OF THIS FILE IS A FLEX-BASIS, NOT A PROMISE.
     .scr is #view-screener, and the shell's `.view { flex: 1 1 auto }` makes it
     GROW to fill <main> — which is sized to the viewport minus the masthead and
     knows nothing about the destination bar fixed over the bottom 53px.  Growth
     happens after `height` is resolved, so the `- var(--shell-tabbar)` on line
     26 was computed and then thrown away: measured, .scr ran to y=844 on an
     844px screen and the status strip — the row count and the active sort —
     sat at 813-844, wholly underneath the nav bar, unreachable in a view that
     does not scroll.  A max-height is not overridden by flex growth: the flex
     algorithm clamps an item to its max and freezes it.  ⛔ Do not "simplify"
     this to a height; the height is what makes the flex-basis right, and this
     is what makes the used size right. */
  .scr {
    max-height: calc(100vh - var(--scr-top) - var(--shell-tabbar));
    max-height: calc(100dvh - var(--scr-top) - var(--shell-tabbar));
  }

  .scr-body { flex-direction: column; }
  .scr-main { order: 1; min-height: 0; }   /* 🔴 see the note above — not optional */
  .scr-rail {
    order: 2;
    flex: 1 1 auto;
    width: auto;
    min-height: 0;
    border-right: 0;
    border-top: var(--hair) solid var(--rule);
  }
  .scr.rail-off .scr-rail { display: none; }
  .scr-rail-inner { padding-bottom: 28px; overscroll-behavior: contain; }

  /* drawer up: the toolbar shrinks to itself and everything under it stands
     down, so the rail owns the rest of the block */
  .scr:not(.rail-off) .scr-main { flex: 0 0 auto; }
  .scr:not(.rail-off) .scr-scroll,
  .scr:not(.rail-off) .scr-active,
  .scr:not(.rail-off) .scr-sql,
  .scr:not(.rail-off) .scr-status { display: none; }

  /* ---- drawer contents: everything is a thumb ---------------------------- */
  .scr-sec-h { min-height: 46px; padding: 8px 12px; gap: 9px; }
  .scr-sec-l { font-size: 12px; letter-spacing: .09em; }
  .scr-sec-caret { font-size: 11px; width: 10px; }
  .scr-sec-badge { font-size: 11.5px; }
  .scr-sec-b { padding: 4px 12px 12px; }

  .scr-opt { min-height: 44px; gap: 11px; padding: 0; }
  .scr-opt input { width: 20px; height: 20px; flex: 0 0 20px; }
  .scr-opt-n { font-size: 13px; }
  .scr-opt-c { font-size: 11.5px; }
  .scr-opts-tall { max-height: 46vh; }

  /* ⚠️ 16px, not 13.  mobile.css forces every INPUT to 16px with a (0,4,1)
     selector, but its rule for <select> and <textarea> is the bare element —
     (0,0,1) — which any class here outranks.  So .scr-optsearch / .scr-num /
     .scr-search input were already 16px and .scr-select was left at 13, which
     is the iOS zoom trap the foundation exists to close (Safari zooms the page
     in on focus below 16px and never zooms back).  Per spec §10.5 the answer
     is to size the BOX, which the min-height and the padding here already do. */
  .scr-optsearch,
  .scr-select,
  .scr-num,
  .scr-search input {
    min-height: 44px;
    padding: 6px 9px;
    font-size: 16px;
  }
  .scr-optsearch, .scr-select { margin-bottom: 8px; }
  .scr-range { gap: 9px; }
  .scr-segbtn { min-height: 44px; font-size: 12px; padding: 4px 6px; }

  /* ---- toolbar ----------------------------------------------------------- */
  .scr-toolbar {
    flex-wrap: wrap;
    gap: 7px;
    padding: 7px 10px;
  }
  .scr-tbbtn,
  .scr-fcount { min-height: 44px; padding: 5px 12px; font-size: 11.5px; }
  .scr-tbbtn { display: inline-flex; align-items: center; }
  .scr-fcount { display: inline-flex; align-items: center; }
  /* NO FILTERS is 117px of disabled button reporting that nothing is
     happening.  On a desk it is a stable landmark in a bar with room to spare;
     on a phone it is a third of a 44px row spent on the absence of news.  It
     comes back the moment it has something to say — as "3 FILTERS", in accent,
     and as the control that clears them (.scr-fcount.on above). */
  .scr-fcount:disabled { display: none; }
  /* find-a-title is the most-used control on the bar, so it takes the first
     line whole instead of being squeezed to 130px between two buttons */
  .scr-search { flex: 1 1 100%; min-width: 0; order: -1; }
  .scr-spacer { display: none; }
  .scr-densbtn { width: 44px; min-height: 44px; font-size: 12px; }

  /* ---- active-filter chips ----------------------------------------------- */
  .scr-active { gap: 6px; padding: 6px 10px; max-height: 96px; }
  .scr-achip { font-size: 12px; line-height: 1; align-items: center; min-height: 40px; }
  .scr-achip-k { display: flex; align-items: center; font-size: 10.5px; }
  .scr-achip-v { display: flex; align-items: center; max-width: 150px; }
  .scr-achip-x { min-width: 40px; font-size: 16px; line-height: 1; }

  /* ---- the table --------------------------------------------------------- */
  /* ⛔ .m-scroll-x IS NOT THE ANSWER HERE, and this is the one place in the
     codebase where that is true.  The utility is the sanctioned container for
     anything wider than the screen (spec §4.1) and it hard-sets
     `overflow-y: hidden`, because the things it was written for — rails,
     bands, strips — are one row tall.  .scr-scroll is the ONLY scrolling
     surface in a view that sizes itself to the viewport: it has to scroll in
     BOTH axes, 1,324px of columns sideways and 2,889 rows down.  Adopting the
     class would silently take the second axis away, which is precisely the
     defect fixed at the top of this block.
     What .m-scroll-x would have GIVEN it, it now has: overflow on the x axis
     (`overflow: auto`, base rule), `overscroll-behavior: contain` (base rule,
     both axes), momentum scrolling, and `min-width: 0` so a flex parent can
     never push the pane wider than its track and take the page sideways with
     it.  The difference is that .scr-scroll does this on the desk too, where
     .m-scroll-x is deliberately inert. */
  .scr-scroll {
    min-width: 0;
    -webkit-overflow-scrolling: touch;
  }

  /* THE SCROLL HAS TO ANNOUNCE ITSELF.  A frozen title column against a
     hairline grid gives no hint that twelve more columns are parked off the
     right edge, and on a phone there is no scrollbar sitting there to say so.
     A short ground-to-nothing fade rides the right edge of the pane: it reads
     as "the table continues", and it retreats the moment the scroller is at
     its end (scroll-driven, so it costs nothing when it is not moving). */
  .scr-scroll { scrollbar-width: none; }
  .scr-scroll::-webkit-scrollbar { height: 0; width: 0; }
  .scr-main { position: relative; }
  .scr-main::after {
    content: "";
    position: absolute;
    inset: 0 0 0 auto;
    width: 26px;
    pointer-events: none;
    z-index: 5;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0), var(--ground));
  }
  /* the fade belongs to the table pane alone: the bars around it lift above
     it rather than the fade guessing their heights */
  .scr-toolbar, .scr-desks, .scr-active, .scr-status, .scr-sql { position: relative; z-index: 6; }
  /* nothing to hint at while the drawer is over the table */
  .scr:not(.rail-off) .scr-main::after { display: none; }

  /* the header row is the sort control, so it stands at thumb height too.
     --headh is read back from the DOM by screener.js (offsetHeight), never
     assumed, so raising it here is safe; --rowh is the one that is not. */
  .scr { --headh: 44px; }
  .scr-table th, .scr-table td { padding: 0 9px; }
  .scr-table tbody td { font-size: 12.5px; }
  .scr-th { font-size: 11px; letter-spacing: .06em; gap: 5px; }
  .scr-th-u { font-size: 11px; }
  .scr-th-a { font-size: 11px; }
  .scr-t-sub, .scr-kind { font-size: 11px; }

  /* the chips are the only per-cell control, so they take the row's height
     and stand apart from each other rather than sitting 3px away */
  .scr-chip {
    min-width: 44px;
    min-height: 44px;
    margin-right: 7px;
    padding: 1px 8px;
    font-size: 11px;
    line-height: 40px;
    text-align: center;
  }

  .scr-status { gap: 12px; padding: 6px 10px 7px; font-size: 11.5px; }
  .scr-empty-h { font-size: 19px; }
  .scr-empty-p { font-size: 13px; }

  /* ---- column picker: a sheet, not a 296px popover ----------------------- */
  .scr-pop {
    position: fixed;
    left: 10px; right: 10px;
    width: auto;
    max-height: 74vh;
    z-index: 70;
  }
  .scr-pop-t { font-size: 13px; }
  .scr-pop-hint { font-size: 12px; padding: 8px 12px; }
  .scr-colitem { min-height: 46px; padding: 2px 12px; gap: 9px; }
  .scr-collab { gap: 9px; }
  .scr-collab input { width: 20px; height: 20px; }
  .scr-collab-t { font-size: 11.5px; }
  .scr-collab-u { font-size: 11px; }
  .scr-grip { font-size: 14px; width: 14px; }
  .scr-mvbtn { width: 40px; min-height: 40px; font-size: 11px; }

  /* ---- SQL console ------------------------------------------------------- */
  .scr-sql { height: 260px; }
  /* a <textarea>, so mobile.css's bare-element rule loses to this class the
     same way .scr-select did — 16px here is the zoom trap, not a type choice */
  .scr-sql-in { font-size: 16px; }
  .scr-sql-title { font-size: 12.5px; }
  .scr-sql-hint { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .scr { transition: none; }
}

/* series status -- ongoing vs completed replaces risk tier for shows (2026-08-09) */
.scr-chip-st{text-transform:lowercase;letter-spacing:.02em}
.scr-st-ongoing{border-color:color-mix(in srgb,var(--ok) 55%,transparent);color:var(--ok)}
.scr-st-completed{border-color:var(--rule);color:var(--muted)}
.scr-st-unknown{border-color:color-mix(in srgb,var(--warn) 45%,transparent);color:var(--warn)}

/* ======================================================================
   MATCH AUDIT — the read-only proposals panel (admin only)

   Deliberately shaped like the STREAMS panel it sits beside: same panel
   chrome, same tag vocabulary, same monospace for anything that is a
   number or a filename.  The one thing it must never look like is a
   control surface — there is no button on a row, because the whole point
   is that NITRATE proposes and John decides.
   ====================================================================== */
.scr-match {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  max-height: 62%;
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-match[hidden] { display: none; }
.scr-match-head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-match-title { font-size: 11px; letter-spacing: .1em; color: var(--ink); }
.scr-match-clock {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.scr-match-sum {
  flex: 0 0 auto;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 1px; background: var(--rule);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-match-stat {
  display: flex; align-items: baseline; gap: 6px;
  padding: 7px 10px; background: var(--panel);
}
.scr-match-stat strong {
  font-family: var(--font-mono); font-size: 15px; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.scr-match-stat span {
  font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted);
}
.scr-match-stat.is-bad strong { color: var(--crit); }

.scr-match-rows { flex: 1 1 auto; min-height: 0; overflow: auto; }
.scr-match-row {
  padding: 9px 10px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-match-row.is-bad  { background: color-mix(in srgb, var(--crit) 7%, var(--panel)); }
.scr-match-row.is-warn { background: color-mix(in srgb, var(--warn) 6%, var(--panel)); }

.scr-match-l1 { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.scr-match-verdict {
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .09em;
  padding: 2px 6px 1px; border: var(--hair) solid var(--rule);
  color: var(--muted); white-space: nowrap;
}
.scr-match-row.is-bad  .scr-match-verdict { color: var(--crit); border-color: var(--crit); font-weight: 700; }
.scr-match-row.is-warn .scr-match-verdict { color: var(--warn); border-color: var(--warn); }
.scr-match-row.is-ok   .scr-match-verdict { color: var(--ok); border-color: var(--ok); }
.scr-match-what { font-size: 13.5px; color: var(--ink); }
.scr-match-kind, .scr-match-conf {
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em;
}

.scr-match-l2 {
  display: flex; align-items: stretch; gap: 8px; flex-wrap: wrap;
  margin-top: 7px;
}
.scr-match-id {
  flex: 1 1 220px; min-width: 0;
  padding: 6px 8px; background: var(--panel-2);
  border: var(--hair) solid var(--rule);
}
.scr-match-arrow { align-self: center; color: var(--muted); font-size: 14px; }
.scr-match-k {
  font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted);
}
.scr-match-name { font-size: 12.5px; color: var(--ink); margin-top: 2px; }
.scr-match-tmdb, .scr-match-ex {
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
  margin-top: 2px; word-break: break-word;
}

.scr-match-l3 {
  display: flex; flex-wrap: wrap; gap: 8px; margin-top: 6px; align-items: baseline;
}
.scr-match-v {
  font-family: var(--font-mono); font-size: 10.5px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
/* a filename is evidence, so it is never truncated into ambiguity */
.scr-match-file {
  margin-top: 4px; padding: 3px 6px;
  font-family: var(--font-mono); font-size: 10px; color: var(--ink-2);
  background: var(--panel-2); border: var(--hair) solid var(--rule);
  word-break: break-all;
}
.scr-match-ev {
  margin: 7px 0 0; font-size: 12px; line-height: 1.45; color: var(--ink-2);
}
.scr-match-sig { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.scr-live-tag.is-primary { color: var(--ink); border-color: var(--ink-2); font-weight: 700; }
.scr-match-why {
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
  margin-top: 3px; line-height: 1.4;
}
.scr-match-foot {
  flex: 0 0 auto;
  margin: 0; padding: 7px 10px 8px;
  border-top: var(--hair) solid var(--rule);
  background: var(--panel-2);
  font-size: 10.5px; line-height: 1.45; color: var(--muted);
}

/* =============================================================== THE DESK
   ADMINDESK_20260819.  Who is signed in, what they are doing, what just
   happened — three panels in one section, drawn in the same film-lab language
   as the STREAMS panel above it and reusing its vocabulary wherever the two
   say the same thing (.scr-live-tag, .scr-modal, the sev-* colours).

   WHY IT IS TABLES AND NOT TILES.  The Screener is a dense table view and the
   density is the point: seven sessions, four streams and a hundred events are
   things you SCAN, and a grid of big cards would fit four of them on a screen
   and call it a dashboard.  Every number here is mono and tabular so a column
   of times lines up on the digit, which is the only reason a table beats a
   list at all.

   COLOUR, AND THE ONE RULE THAT GOVERNS IT.  Safety orange means SELECTED /
   NEEDS ATTENTION and nothing else, so in this whole block it appears exactly
   twice: on the YOU chip (this row is you — the selected one) and on the left
   rule of a cast row, which is the STREAMS panel's own mark for the same
   thing.  Everything that is merely INFORMATION is drawn in ink, ink-2 and
   muted.  A failure is --crit, a warning --warn, good news --ok, exactly as
   the sibling panel already does it.

   THE PHONE.  Below 760px every table stops being a table: each row becomes a
   block and each cell carries its own label from data-k.  A seven-column
   register cannot be made to fit 390px by shrinking it — it can only be
   turned on its side, and nothing here scrolls sideways.
   ------------------------------------------------------------------------ */
.scr-desk {
  flex: 0 0 auto;
  display: flex; flex-direction: column;
  max-height: 66%;
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-desk[hidden] { display: none; }

.scr-desk-head {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-desk-title { font-size: 11px; letter-spacing: .1em; color: var(--ink); }
.scr-desk-clock {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* Two columns on a desk: the register and what it is doing on the left, the
   trail on the right, because the trail is long and wants the height while the
   other two are short and wide.  One column the moment that stops fitting. */
.scr-desk-body {
  flex: 1 1 auto; min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 1px;
  background: var(--rule);
  overflow: hidden;
}
.scr-desk-col {
  min-width: 0; min-height: 0;
  display: flex; flex-direction: column;
  background: var(--panel);
  overflow: hidden;
}

/* ⭐ EACH PANEL SCROLLS ITSELF.  The first draft let the COLUMN scroll, and on
   a real desk — seven sessions in the register — that pushed DOING NOW clean
   off the bottom of the panel: the second most important thing on the page was
   invisible until you scrolled for it.  Every panel is now a flex box with its
   own scrolling body, so a long register shortens itself instead of evicting
   its neighbour, and the two headers are always on screen. */
.scr-desk-p {
  min-width: 0; min-height: 0;
  flex: 0 1 auto;
  display: flex; flex-direction: column;
}
.scr-desk-p + .scr-desk-p { border-top: var(--hair) solid var(--rule); }
/* See paintDoing(): a ceiling on the register only while something is playing.
   ⚠️ :not(:last-child) is load-bearing. The trail is the first AND last panel of
   its own column, so without it the ceiling capped the trail too and left a
   third of the right-hand column as empty paper. */
.scr-desk.doing-busy .scr-desk-col > .scr-desk-p:first-child:not(:last-child) {
  max-height: 55%;
}
/* the last panel in a column takes the slack, so the trail uses the full height
   of its column instead of stopping halfway down with empty paper under it */
.scr-desk-col > .scr-desk-p:last-child { flex: 1 1 auto; }
.scr-desk-ph {
  flex: 0 0 auto;
  display: flex; align-items: baseline; gap: 7px;
  padding: 6px 10px 5px;
  background: var(--panel-2);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-desk-pk {
  font-family: var(--font-display); font-weight: 800; font-size: 10px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink);
}
.scr-desk-pn {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink-2);
}
.scr-desk-pnote {
  font-size: 10px; color: var(--muted); text-align: right;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.scr-desk-pb {
  flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
}

/* ---- the tables ---------------------------------------------------------- */
.scr-desk-t {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}
.scr-desk-t th {
  position: sticky; top: 0; z-index: 1;
  padding: 4px 8px 3px;
  text-align: left;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 400;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
  background: var(--panel);
  border-bottom: var(--hair) solid var(--rule);
  white-space: nowrap;
}
.scr-desk-t td {
  padding: 5px 8px;
  vertical-align: top;
  border-bottom: var(--hair) solid var(--rule);
  font-size: 12px; line-height: 1.35; color: var(--ink-2);
  overflow-wrap: anywhere;
}
.scr-desk-r:hover td { background: var(--panel-2); }
/* an expired row is history, not a person — it recedes */
.scr-desk-r.is-dead td { color: var(--muted); }
.scr-desk-r.is-bad td { background: color-mix(in srgb, var(--crit) 7%, var(--panel)); }
/* ⭐ THE ONE ORANGE ON THIS PANEL: the row that is YOU is the selected row */
.scr-desk-r.is-self td { box-shadow: inset 2px 0 0 var(--accent); }
.scr-desk-r.is-cast td { box-shadow: inset 2px 0 0 var(--accent); }

.scr-desk-who { color: var(--ink); }
.scr-desk-who > span + span { margin-left: 6px; }
.scr-desk-what { color: var(--ink); }
.scr-desk-t td.scr-desk-when {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
}
.scr-desk-abs { font-size: 11px; color: var(--ink-2); white-space: nowrap; }
.scr-desk-rel {
  display: block;
  font-size: 10px; color: var(--muted); white-space: nowrap;
}
/* ⚠️ THE ROUTE IS THE ONE LONG THING IN A NARROW COLUMN.  "GET /api/admin/
   viewers" measures ~132px in an 89px cell, and a nowrap span in a fixed table
   cell does not clip — it spills, and the panel body grows a sideways scroll
   nobody asked for.  Only this line wraps; the short spans beside it must not,
   because "7d" and "ago" on two lines is worse than either. */
.scr-desk-wrap { white-space: normal; overflow-wrap: anywhere; }
.scr-desk-when.is-soon .scr-desk-abs,
.scr-desk-when.is-soon .scr-desk-rel { color: var(--warn); }
.scr-desk-when.is-gone .scr-desk-abs { color: var(--muted); }
.scr-desk-ip {
  display: block;
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
}
.scr-desk-ref {
  display: block;
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .04em;
  color: var(--muted);
}
.scr-desk-route, .scr-desk-box { line-height: 1.9; }
/* ⚠️ A TAG MAY WRAP HERE AND MAY NOT ELSEWHERE.  .scr-live-tag is nowrap because
   on the STREAMS panel it sits in a flex row with room to spare; in a fixed
   table column "meaneye · libx264 (CPU)" is wider than its cell, and a nowrap
   tag in a fixed cell is how a table takes a page sideways. */
.scr-desk-box .scr-live-tag { white-space: normal; }
.scr-desk-route .scr-live-tag + .scr-live-tag,
.scr-desk-box .scr-live-tag + .scr-live-tag { margin-left: 4px; }
.scr-desk-act { text-align: right; white-space: nowrap; }
/* the action column is the narrowest in the table and the button has to live
   inside it, so it gives back the toolbar's generous padding */
.scr-desk-act .scr-tbbtn { padding: 3px 6px 2px; white-space: normal; }
/* ⚠️ AND THE TAGS SHRINK A HALF-STEP.  "TRANSCODE" is one unbreakable word and
   at the toolbar's tag size it is wider than the ROUTE column; a word that
   cannot wrap and cannot shrink is a column that goes sideways. */
.scr-desk-route .scr-live-tag { font-size: 9.5px; letter-spacing: .02em; padding: 2px 4px 1px; }
/* ⚠️ THIS IS YOU and REPORT ONLY are sentences, not labels, and a nowrap
   sentence in the narrowest column of a fixed table walks straight out of it —
   measured at 81px in a 66px cell. In this one place they wrap. */
.scr-desk-act .scr-desk-ro { white-space: normal; display: inline-block; }
.scr-desk-rev:hover { color: var(--crit); border-color: var(--crit); }
.scr-desk-err {
  margin-top: 4px;
  font-family: var(--font-mono); font-size: 10px; color: var(--crit);
  white-space: normal; text-align: left;
}

/* a small square chip: a role, an event kind, a caveat on a row */
.scr-desk-chip {
  display: inline-block;
  padding: 1px 5px 0;
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-2);
  background: var(--panel-2); border: var(--hair) solid var(--rule);
  white-space: nowrap;
}
.scr-desk-chip.is-admin { color: var(--ink); border-color: var(--ink-2); font-weight: 700; }
.scr-desk-chip.is-warn { color: var(--warn); border-color: var(--warn); }
.scr-desk-chip.is-in { color: var(--ok); border-color: var(--ok); }
.scr-desk-chip.is-out { color: var(--muted); }
.scr-desk-chip.is-play { color: var(--ink); border-color: var(--ink-2); }
.scr-desk-chip.is-stop { color: var(--crit); border-color: var(--crit); }
/* ⭐ THIS IS YOU — the selected row, and the only place orange means a person */
.scr-desk-you {
  display: inline-block;
  padding: 1px 5px 0;
  font-family: var(--font-mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .1em;
  color: var(--accent); border: var(--hair) solid var(--accent);
}
.scr-desk-dev {
  display: inline-block;
  padding: 1px 5px 0;
  font-family: var(--font-mono); font-size: 9.5px;
  color: var(--muted); background: var(--panel-2);
  border: var(--hair) solid var(--rule);
}
.scr-desk-dev.is-harness { color: var(--warn); border-color: var(--warn); }
/* what cannot be done from here, said instead of a button that would not work */
.scr-desk-ro {
  display: inline-block;
  padding: 1px 5px 0;
  font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .06em;
  color: var(--muted); border: var(--hair) dashed var(--rule);
  white-space: nowrap;
}

/* 🔴 THE HOUSE RULE MADE VISIBLE.  `not known` is set in italic muted so it can
   never be mistaken for a value, and it is deliberately WORDS: a 0 or an em
   dash in a numeric column reads as a measurement, and an unknown that reads
   as a measurement is the "0 DRIVES" failure this estate keeps repeating. */
.scr-desk-unk {
  font-family: var(--font-ui); font-style: italic; font-size: 11px;
  color: var(--muted);
}
.scr-desk-tt { color: var(--ink); }
.scr-desk-dt {
  display: block;
  font-family: var(--font-mono); font-size: 10px; color: var(--muted);
}

.scr-desk-msg {
  padding: 8px 10px;
  font-size: 10.5px; line-height: 1.45; color: var(--muted);
  border-bottom: var(--hair) solid var(--rule);
}
.scr-desk-msg.sev-crit { color: var(--crit); }
.scr-desk-msg.sev-warn { color: var(--warn); }

/* ⚠️ THERE IS NO EXPIRED TABLE, and that is the API's decision rather than an
   omission: /api/admin/viewers hands over a COUNT of expired sessions and never
   their rows, because an expired session is not a person at a screen. The count
   is drawn as a sentence in the panel, which is the whole of "separately, or
   not at all, but never silently mixed in". */

.scr-desk-foot {
  flex: 0 0 auto;
  margin: 0; padding: 7px 10px 8px;
  border-top: var(--hair) solid var(--rule);
  background: var(--panel-2);
  font-size: 10.5px; line-height: 1.45; color: var(--muted);
}

@media (max-width: 1080px) {
  /* one column: the trail moves below the register rather than beside it, and
     the whole desk becomes one scroller instead of three.
     🔴 display:block, NOT a one-column grid.  Measured at 390px: a grid with a
     definite height and two auto rows gave each row HALF the container and let
     the content run out of its own row — the register's rows painted straight
     over the trail's header.  A block box takes its content's height, which is
     the whole point of scrolling the desk as one document down here. */
  .scr-desk-body { display: block; overflow-y: auto; }
  .scr-desk-col + .scr-desk-col { border-top: var(--hair) solid var(--rule); }
  .scr-desk-col { overflow: visible; display: block; }
  .scr-desk-p { display: block; }
  .scr-desk-pb { overflow: visible; }
  .scr-desk-t th { position: static; }
  /* 🔴 AND THE CEILING COMES OFF.  In one column a panel has no scroller of its
     own, so a max-height there does not shorten the panel — it clips nothing
     and lets the register's rows run straight underneath the DOING NOW header.
     Measured at 390px: the two panels drew on top of each other. */
  .scr-desk.doing-busy .scr-desk-col > .scr-desk-p:first-child:not(:last-child) {
    max-height: none;
  }
}

@media (max-width: 760px) {
  .scr-desk { max-height: 74%; }
  /* the table pane's edge fade belongs to the table pane, not over this */
  .scr-desk { position: relative; z-index: 6; }

  /* ⛔ NOT A SCROLLER.  A seven-column register at 390px is 900px wide however
     it is squeezed, and .m-scroll-x would only let it go sideways — which the
     mobile spec forbids on a page body.  So the table is turned on its side:
     every row is a block, every cell carries the label its column heading used
     to carry, and the whole thing reads as a stack of records.  Nothing here
     scrolls sideways at any width. */
  .scr-desk-t, .scr-desk-t tbody, .scr-desk-t tr, .scr-desk-t td { display: block; }
  .scr-desk-t thead { display: none; }
  .scr-desk-t { table-layout: auto; }
  .scr-desk-t tr {
    padding: 7px 10px 8px;
    border-bottom: var(--hair) solid var(--rule);
  }
  /* the label takes a fixed gutter and the cell's contents wrap beside it —
     ⚠️ flex-wrap is not decoration: without it a long account name and its three
     chips are squeezed into one unbreakable row and the name breaks a letter at
     a time down the cell */
  .scr-desk-t td {
    padding: 1px 0;
    border: 0;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 8px;
  }
  .scr-desk-t td[data-k]::before {
    content: attr(data-k);
    flex: 0 0 84px;
    font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .1em;
    color: var(--muted);
  }
  .scr-desk-t td > * { min-width: 0; }
  .scr-desk-t td.scr-desk-who,
  .scr-desk-t td.scr-desk-what { font-size: 13px; }
  .scr-desk-abs, .scr-desk-rel { display: inline; }
  .scr-desk-rel { margin-left: 6px; }
  .scr-desk-ip { display: inline; margin-left: 6px; }
  .scr-desk-ref { display: inline; }
  .scr-desk-dt { display: inline; margin-left: 6px; }
  .scr-desk-act {
    display: block; text-align: left; margin-top: 6px;
  }
  .scr-desk-r.is-self td, .scr-desk-r.is-cast td { box-shadow: none; }
  .scr-desk-r.is-self, .scr-desk-r.is-cast { box-shadow: inset 2px 0 0 var(--accent); }
  .scr-desk-r.is-bad td { background: none; }
  .scr-desk-r.is-bad { background: color-mix(in srgb, var(--crit) 7%, var(--panel)); }
  .scr-desk-r:hover td { background: none; }
  /* every control on a phone is a thumb target */
  .scr-desk-act .scr-tbbtn { min-height: 44px; padding: 5px 12px; font-size: 11.5px; }
  .scr-desk-pnote { display: none; }
}

/* ===================================================================== */
/*  ZOMBIES_20260820 — the request backlog panel on the desk.            */
/*  Safety orange stays what it is everywhere else in this house: THIS   */
/*  NEEDS ATTENTION.  It is on the actionable chip and on a row that has */
/*  waited past the staleness floor, and on nothing else — a backlog     */
/*  where every line is orange is a backlog nobody reads.                */
/* ===================================================================== */
.scr-zb-sum { display: flex; flex-wrap: wrap; gap: 4px 6px; margin: 0 0 8px; }
.scr-zb-chip {
  font: 500 10.5px/1.5 var(--font-ui);
  letter-spacing: .04em;
  padding: 2px 6px;
  border: 1px solid var(--rule);
  color: var(--ink-2);
  white-space: nowrap;
}
.scr-zb-chip b { font-variant-numeric: tabular-nums; color: var(--ink); }
.scr-zb-chip.is-do { border-color: var(--accent); color: var(--accent); }
.scr-zb-chip.is-do b { color: var(--accent); }

.scr-zb-head {
  display: flex; align-items: center; gap: 8px;
  font: 400 11px/1.6 var(--font-ui);
  color: var(--ink-2);
  margin: 0 0 6px;
}
.scr-zb-head > span { flex: 1 1 auto; }
.scr-zb-refresh {
  font: 500 10px/1 var(--font-ui); letter-spacing: .08em;
  padding: 5px 8px; min-height: 26px;
  background: transparent; color: var(--ink-2);
  border: 1px solid var(--rule); cursor: pointer;
}
.scr-zb-refresh:hover { color: var(--ink); border-color: var(--ink-3); }

.scr-zb-age { font-variant-numeric: tabular-nums; color: var(--ink-2); white-space: nowrap; }
tr.is-stale .scr-zb-age { color: var(--accent); }
.scr-zb-title { color: var(--ink); }
.scr-zb-year  { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.scr-zb-who   { color: var(--ink-2); }
.scr-zb-why   { color: var(--ink-3); font-size: 10.5px; }

.scr-zb-act {
  font: 500 10px/1 var(--font-ui); letter-spacing: .06em;
  padding: 5px 7px; min-height: 26px; width: 100%;
  background: transparent; color: var(--accent);
  border: 1px solid var(--accent); cursor: pointer;
}
.scr-zb-act:hover:not(:disabled) { background: var(--accent); color: var(--accent-ink); }
.scr-zb-act:disabled { cursor: default; }
/* ⭐ Done and refused are DIFFERENT, and neither is an error state: a refusal
   is the server re-deriving the class and declining, which is the interlock
   working. It reads as a quiet fact, not as a red failure. */
.scr-zb-act.is-done    { color: var(--ink-3); border-color: var(--rule); }
.scr-zb-act.is-refused { color: var(--ink-2); border-color: var(--ink-3); border-style: dashed; }

@media (max-width: 760px) {
  .scr-zb-why { display: none; }        /* the chip row above already says it */
  .scr-zb-head { flex-wrap: wrap; }
}

/* 🔴 var(--paper) IS NOT A TOKEN. It is defined in no stylesheet in this
   project - tokens.css has --ground / --panel / --panel-2 / --ink / --accent /
   --accent-ink and nothing called paper - so every var(--paper) here resolved
   to nothing and the property fell back to the INHERITED colour. On an accent
   fill that meant orange text on orange: the REQUESTS desk carried it on its
   button hover before this section existed.
   ⭐ --accent-ink is the token for ink ON an accent fill, and it flips with the
   theme (#ffffff light, #1a0f04 dark). --ground works the same way against
   --ink-2, because both sides of that pair flip together. */
/* ================================================== SCREENERDESKS_20260825 */
/* The desks, on a line of their own beneath the table controls. Read as tabs
   because that is what they now are: one is open, the rest are where you
   could go instead. ⛔ No box, no fill - a rule under the strip and a heavier
   rule under the open one. The Screener is a light table, not an app chrome. */
.scr-desks {
  /* ⚠ .scr-main is a COLUMN flexbox and the table below wants every pixel, so
     without this the strip is shrunk to a hairline - which is exactly what it
     did on the first cut. .scr-toolbar carries the same line for the same
     reason. */
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  gap: 2px;
  padding: 0 8px;
  border-bottom: var(--hair) solid var(--rule);
  background: var(--ground);
  overflow-x: auto;
  scrollbar-width: none;
}
.scr-desks::-webkit-scrollbar { display: none; }

.scr-desks .scr-tbbtn {
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;          /* sit ON the strip's rule, not above it */
  background: transparent;
  padding: 7px 10px;
  white-space: nowrap;
}
.scr-desks .scr-tbbtn:hover { color: var(--ink); border-bottom-color: var(--ink-3); }
/* ⭐ The open desk is the only one wearing the accent, and it is a rule rather
   than a fill: one filled control per view is the house limit and the table
   below spends it. */
.scr-desks .scr-tbbtn.on {
  color: var(--accent);
  border-bottom-color: var(--accent);
  background: transparent;
}

@media (max-width: 760px) {
  .scr-desks { padding: 0 4px; }
  .scr-desks .scr-tbbtn { padding: 8px 8px; }
}

/* ==================================================== ACCOUNTDESK_20260825 */
/* THE DOOR. Same shell as REQUESTS below it, so the Screener keeps one
   grammar for "a thing you open from the toolbar". */
.scr-acct {
  border: var(--hair) solid var(--rule);
  border-top: 0;
  background: var(--panel);
}
.scr-acct-body { padding: 10px 12px 12px; max-height: 62vh; overflow: auto; }

/* The count of people waiting, carried on the toolbar button itself. ⭐ It is
   the only tally in this toolbar, so it does not need to compete — it is a
   filled accent only when it is non-zero, and it is not drawn at all at zero
   (a grey 0 beside every other button is a thing the eye learns to skip). */
.scr-acct-tally {
  display: inline-block;
  margin-left: 6px;
  min-width: 14px;
  padding: 0 3px;
  font: 600 9.5px/14px var(--font-ui);
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--accent-ink);
  background: var(--accent);
}
/* ⚠ A DISPLAY VALUE BEATS THE BROWSER'S OWN [hidden] RULE, so hiding this at
   zero needs saying explicitly - without it the badge sat there reading 0 for
   an empty queue, which is the one thing it must never do. */
.scr-acct-tally[hidden] { display: none; }
/* 🔴 "could not read it" is NOT zero and must not look like a count. */
.scr-acct-tally.is-unknown {
  color: var(--ink-2);
  background: transparent;
  border: 1px dashed var(--ink-3);
}

.scr-acct-h {
  font: 600 10px/1.4 var(--font-ui);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 12px 0 6px;
  padding-bottom: 4px;
  border-bottom: var(--hair) solid var(--rule);
}
.scr-acct-h:first-child { margin-top: 0; }

.scr-acct-name { color: var(--ink); }
.scr-acct-disp { color: var(--ink-3); margin-left: 6px; }
/* VIP_20260905 — the tier tag on an account row */
.scr-acct-vip { margin-left: 6px; font: 500 9px/1 var(--font-ui); letter-spacing: .12em;
                padding: 2px 4px; border: 1px solid var(--accent); color: var(--accent);
                vertical-align: middle; }
.scr-acct-when { color: var(--ink-2); font-variant-numeric: tabular-nums; white-space: nowrap; }
.scr-acct-never { color: var(--ink-3); font-style: italic; }
.scr-acct-fail { text-align: center; }
.scr-acct-failn {
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  font-weight: 600;
}
/* ⚠ Buttons are capped rather than stretched: a lone SUSPEND filling the
   whole action column reads as a mistake beside the two-button rows above it,
   and a wide destructive button is a wide target. */
/* ⚠️ 2026-09-01, John: "the suspended UI broken" — the verbs pair (SUSPEND +
 * SIGN OUT EVERYWHERE, ~235px) outgrew its 14rem column when the second verb
 * landed on 08-28, and an unwrappable flex row SPILLS past the panel edge and
 * clips.  Wrapping keeps every word (the masthead rule) and fits any width:
 * the widest single button is ~9.5rem, well inside the column. */
.scr-acct-acts { display: flex; flex-wrap: wrap; gap: 5px; padding: 4px 0;
                 justify-content: flex-end; }

.scr-acct-act {
  font: 500 10px/1 var(--font-ui); letter-spacing: .06em;
  /* ⚠️ was `flex: 1 1 0; max-width: 6.5rem` — sized for the short verbs of
   * 08-25 (LET IN, REFUSE, SUSPEND). SIGN OUT EVERYWHERE (08-28) never fit
   * that cap and clipped mid-word inside its own border ever since — John,
   * 2026-09-01: "the suspended UI broken". A verb button sizes to its label
   * and never shrinks; the row's flex-wrap takes the strain instead. */
  padding: 5px 7px; min-height: 26px; flex: 0 0 auto;
  background: transparent; color: var(--ink-2);
  border: 1px solid var(--rule); cursor: pointer;
  white-space: nowrap;
}
.scr-acct-act:hover:not(:disabled) { color: var(--ink); border-color: var(--ink-3); }
.scr-acct-act:disabled { cursor: default; }
/* The one verb this section exists for gets the accent; the ones that end
   something do not, until they are armed. */
.scr-acct-act.is-go { color: var(--accent); border-color: var(--accent); }
.scr-acct-act.is-go:hover:not(:disabled) { background: var(--accent); color: var(--accent-ink); }
.scr-acct-act.is-armed {
  color: var(--ground); background: var(--ink-2); border-color: var(--ink-2);
}
.scr-acct-act.is-refused { color: var(--ink-2); border-style: dashed; }

tr.is-waiting .scr-acct-name { color: var(--accent); }
tr.is-susp .scr-acct-name { color: var(--ink-3); text-decoration: line-through; }

@media (max-width: 760px) {
  .scr-acct-when { font-size: 10px; }
  .scr-acct-acts { flex-wrap: wrap; }
}

/* ⭐ ITS OWN SECTION, not a panel on the desk. John, 2026-08-20: "why not just
   create another section like watches, streams?? it's getting clusterfucked
   there". The desk is a live register on a 4 s heartbeat; this is a queue of
   work read once on demand. Same shell as STREAMS and MATCHES so the Screener
   keeps one grammar for "a thing you open from the toolbar". */
.scr-zb {
  border: var(--hair) solid var(--rule);
  border-top: 0;
  background: var(--panel);
}
.scr-zb-body {
  padding: 10px 12px 12px;
  max-height: 62vh;
  overflow: auto;
}

/* ── ASKSDESK_20260828 — the review queue as a Screener section ───────────────
   John: "in screener make another section button instead of going down like
   that when i click other function". The desk itself is drawn by wanted.js in
   deskOnly mode and brings its own .wnt-dk-* styling, so this only needs the
   frame its five siblings have — copied from .scr-acct rather than invented,
   so the six sections cannot drift apart. */
.scr-asks {
  border: var(--hair) solid var(--rule);
  border-top: 0;
  background: var(--panel);
}
.scr-asks-body { padding: 10px 12px 12px; max-height: 62vh; overflow: auto; }
/* ⛔ The desk's own header STAYS. It carries the waiting count and the sentence
   that says what approving actually does — one line of duplicated wording is a
   far smaller cost than a queue whose consequences are unstated. */
.scr-asks-body .wnt-desk { display: block; }
.scr-asks-none {
  margin: 0; padding: 6px 2px;
  font-family: var(--font-ui);
  font-size: 12px; line-height: 1.5; color: var(--dim);
}
.scr-asks-none.is-bad { color: var(--crit); }   /* AUDIT_20260828: a queue that could not be read */

/* ── LIBRARY_20260828 — the catalogue is a section, so it can be put away ─────
   John: "stream is stream , watches is watches. make a section called library
   instead for those list."  Everything the TABLE owns goes away together: its
   whole toolbar row (filters, search, columns, density, CSV — the file's own
   comment already calls these one family), the facet rail beside it, the active
   filter chips, the table itself and its status bar.
   ⛔ Selectors are two classes deep so they beat the members' own rules without
   !important, and nothing here touches `hidden` — each element keeps deciding
   its own visibility for when the library comes back. */
.scr.is-section .scr-toolbar,
.scr.is-section .scr-rail,
.scr.is-section .scr-active,
.scr.is-section .scr-scroll,
.scr.is-section .scr-status { display: none; }

/* the desk row is the only thing that never goes away, so it carries the seam */
.scr.is-section .scr-desks { margin-top: 0; }

/* HEALTH_20260828 — the house's numbers, one line under the desk row */
.scr-health { display: flex; flex-wrap: wrap; gap: 4px 16px; padding: 4px 10px;
  font-size: 11px; color: var(--muted); background: var(--panel); border-bottom: 1px solid var(--rule); }
.scr-hchip { white-space: nowrap; letter-spacing: 0.02em; }
.scr-hchip b { color: var(--ink); font-weight: 600; }
.scr-hchip.is-bad, .scr-hchip.is-bad b { color: var(--crit); }

/* PROFILE_20260901 — a face beside the account, on the owner's desks only */
.scr-acct-face{
  width:22px; height:22px; object-fit:cover; vertical-align:middle;
  margin-right:7px; border:1px solid var(--rule);
}

/* ── CANCEL_20260909 — DOWNLOADS, its own section ────────────────────────────
   John: "add cancel button in screener". Built on the REQUESTS section's shell
   and its button, deliberately: the two desks answer neighbouring questions and
   a viewer who has learned one has learned the other. */
.scr-dl {
  border: var(--hair) solid var(--rule);
  border-top: 0;
  background: var(--panel);
}
.scr-dl-body { padding: 10px 12px 12px; max-height: 62vh; overflow: auto; }
.scr-dl-table { width: 100%; border-collapse: collapse; }
.scr-dl-table th {
  font: 500 9.5px/1 var(--font-ui); letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-3); text-align: left; padding: 0 8px 6px 0; font-weight: 500;
}
.scr-dl-table td { padding: 6px 8px 6px 0; vertical-align: middle; border-top: var(--hair) solid var(--rule); }
.scr-dl-table td:last-child, .scr-dl-table th:last-child { padding-right: 0; width: 1%; }

.scr-dl-kind {
  font: 500 9px/1 var(--font-ui); letter-spacing: .1em;
  color: var(--ink-3); white-space: nowrap;
}
.scr-dl-title  { color: var(--ink); }
.scr-dl-season { color: var(--accent); font-variant-numeric: tabular-nums; }
.scr-dl-year   { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.scr-dl-state  { color: var(--ink-3); font-size: 10.5px; white-space: nowrap; }
.scr-dl-pct    { color: var(--ink-2); font-variant-numeric: tabular-nums; font-size: 10.5px; }
.scr-dl-of     { color: var(--ink-3); font-variant-numeric: tabular-nums; font-size: 10px; }

/* A bar, not a spinner: the question is "how far has this got", and the answer
   is a length. ⛔ No transition — the desk repaints on a read, and a width that
   animates from 0 every time would read as progress being lost. */
.scr-dl-bar {
  display: inline-block; width: 84px; height: 4px;
  background: var(--rule); vertical-align: middle;
}
.scr-dl-bar i { display: block; height: 100%; background: var(--accent); }

/* A wedged row says so in the one place the eye is already going. */
tr.is-sick .scr-dl-state { color: var(--accent); }
tr.is-sick .scr-dl-bar i { background: var(--ink-3); }
/* Cancelled: struck through and quiet, until the next read removes it. */
tr.is-gone .scr-dl-title,
tr.is-gone .scr-dl-season { color: var(--ink-3); text-decoration: line-through; }

/* CANCEL is the verb that ends something, so it is NOT the accent until it has
   been armed — the same rule the accounts desk keeps. */
.scr-dl .scr-zb-act.is-cut { color: var(--ink-2); border-color: var(--ink-3); }
.scr-dl .scr-zb-act.is-cut:hover:not(:disabled) { color: var(--ink); border-color: var(--ink); background: transparent; }
.scr-dl .scr-zb-act.is-armed {
  color: var(--ground); background: var(--ink-2); border-color: var(--ink-2);
}

@media (max-width: 760px) {
  .scr-dl-of { display: none; }
  .scr-dl-bar { width: 48px; }
}

/* ── DROPSERIES_20260909 — the waiting half, and the boundary John asked for ──
   "there has to be a clear boundaries between completed, ongoing, premiere
   series". One verb, three labels: the badge is what tells him what the press
   will cost, so it reads before the title, not after it. */
.scr-wt-head {
  display: flex; align-items: baseline; gap: 8px;
  padding: 12px 12px 0; border-top: var(--hair) solid var(--rule);
}
.scr-wt-title {
  margin: 0; font: 500 10px/1 var(--font-ui); letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3);
}
.scr-wt-body { max-height: 46vh; }

.scr-wt-state {
  font: 500 9px/1 var(--font-ui); letter-spacing: .1em; white-space: nowrap;
}
/* ⭐ PREMIERE carries the accent because it is the safe one and the one he came
   here for: nothing aired, nothing held, nothing lost. ONGOING is the only case
   that costs something he might want, so it is the one that stands out as a
   warning rather than as a verb. COMPLETED is quiet — nothing new was coming. */
.scr-wt-state.is-premiere  { color: var(--accent); }
.scr-wt-state.is-ongoing   { color: var(--ink); }
.scr-wt-state.is-completed { color: var(--ink-3); }

.scr-zb-chip.is-premiere  { border-color: var(--accent); color: var(--accent); }
.scr-zb-chip.is-premiere b { color: var(--accent); }

.scr-wt-sealed { color: var(--ink-3); font-size: 10.5px; }
tr.is-sealed .scr-dl-title { color: var(--ink-3); }
tr.is-sealed .scr-wt-state { opacity: .5; }
