/* toi/web/styles.css — a television, not a web page.
 *
 * Modern but nostalgic: near-black with a warm cast, amber phosphor for the
 * things a CRT would have glowed, one cool accent for channel numbers, and
 * restraint everywhere else. No gradients for their own sake, no purple.
 * Tabular numerals throughout, because a clock that reflows is a clock you
 * cannot read at a glance.
 */
:root{
  /* ONE palette, used by the guide AND every piece of chrome. The picture
     sits on black because video must; everything else is paper, sky, sage and
     terracotta so the set reads as one object rather than two. */
  --bg:#0c0b09;            /* behind the picture only            */
  --panel:#f6f0e2;
  --raise:#ece4d0;
  --ink:#463f34;           /* warm ink, never black              */
  --dim:#6f6455;
  --faint:#968975;
  --line:rgba(70,63,52,.18);
  --line-2:rgba(70,63,52,.10);
  --amber:#c87f5c;         /* terracotta carries the accent      */
  --amber-dim:#a9613f;
  --cyan:#6fa2bd;
  --tv-cyan:#63d2dd;       /* the on-screen display, which is a TUBE */
  --tv-amber:#ffb23a;
  --cab:#1b1915; --cab-2:#26231e;      /* the set's cabinet: its front panel */
  --fp-h:calc(64px + env(safe-area-inset-bottom,0px)); /* the panel's height: 10 + key 30 + 5 + label 9 + 10 */
  --key:#2f2b25; --key-hi:#3a352e; --engrave:#a89d88;
  /* the pages MENU opens: the front panel's graphite (the owner, 2026-10-25) */
  --pg:#211e19; --pg-line:#38322a; --pg-sel:#2b2721; --pg-ink:#ece6da;
  --pg-soft:#b9ae9a; --pg-fill:#e4dccb; --pg-amber:#e3953f;
  /* ONE PLACE for the list and every page it opens: bottom right, just above
   * the front panel (the owner, 2026-10-26: "the menu list should pop up in
   * the bottom right. then the audio video and stats should pop up in similar
   * places") */
  --pg-right:4vw; --pg-bottom:calc(var(--fp-h) + 2vh);
  --ok:#5c8f6b; --warn:#b8862f; --bad:#b0453c;
  /* guide — soft painted palette: sky, paper, sage, terracotta. Flat washes
     and generous air rather than hard chassis edges; nostalgic in the COLOUR,
     modern in the craft (no bevels, no texture overlays, crisp type). */
  --sky:#9dc3d6; --sky-deep:#6fa2bd; --sky-pale:#d6e7ef;
  --paper:#f6f0e2; --paper-2:#ece4d0; --paper-3:#e0d5bd;
  --gink:#463f34; --gdim:#6f6455; --gfaint:#9689756; --gfaint:#968975;
  --grule:rgba(70,63,52,.16);
  --sage:#93ab7c; --sage-deep:#6f8a5c;
  --terra:#c87f5c; --terra-deep:#a9613f;
  --chassis:var(--paper); --gpanel:var(--paper-2); --gpanel2:var(--paper);
  /* ── THE GUIDE'S OWN SET ───────────────────────────────────────────
     A dark ground and ONE accent.

     It used to cycle three block colours down the rows — teal, blue, sand —
     which reads as decoration rather than information: the colour changed for
     reasons the viewer cannot use, and the eye has to work out that it means
     nothing. Every real EPG does the opposite: a calm dark ground, one
     highlight that means "this is you", one red line that means "this is now".
     Colour carries meaning or it is noise.

     Dark, not warm grey, because the guide is on a television in a room with
     the lights down, and because the picture behind it is black. */
  --g-grey:#0d131f;        /* the ground                              */
  --g-grey-2:#121a28;      /* the track a programme sits on           */
  --g-band:#0a1017;        /* channel column and masthead             */
  --g-band-2:#141d2c;      /* the time ruler                          */
  --g-block:#1b2536;       /* a programme                             */
  --g-block-2:#212d42;     /* alternating, for row separation only    */
  --g-sel:#1e63c8;         /* THE accent: the channel you are on      */
  --g-sel-2:#2a76e0;
  --g-cream:#eef3fa; --g-ink:#0b1119;
  --g-red:#ff4d3d;         /* now, and only now                       */
  --g-yellow:#ffc65c;      /* channel numbers                         */
  --g-dim:#8fa2bd;
  --g-teal:#1b2536; --g-blue:#212d42; --g-sand:#1b2536;
  --chcol:minmax(150px,14vw);
  --red:var(--terra-deep); --blue:var(--sky-deep); --green:var(--sage-deep);
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif;
}
*{box-sizing:border-box}
html,body{height:100%;margin:0;background:var(--bg);color:var(--ink);
  font:14px/1.45 var(--sans);overflow:hidden;-webkit-font-smoothing:antialiased}

#tv{position:fixed;inset:0;background:var(--bg);display:grid;place-items:center;overflow:hidden}

/* ── the picture ──────────────────────────────────────────────────────── */
/* The picture is CENTRED in the frame and never enlarged past what the source
 * can carry — modules/screen/h264.mjs sets the ceiling from the frame's own
 * size. Filling the screen is not the same as looking good: a 360p source
 * stretched to a 1920 window is three times its own size and three times as
 * soft. Broadcast pillarboxes an SD feed rather than stretching it.
 * Its box is the frame itself (absolute, inset 0): as the grid's one item, in
 * an auto row, it took its own 16:9 height at the window's width, and every
 * window wider than 16:9 cut the picture's lower edge off (2026-10-17). */
/* The glass ends where the front panel begins (the owner, 2026-10-22: "it
 * shouldnt go off the page"): the panel shows beneath the picture, never over it. */
#screen{display:none;position:absolute;inset:0 0 var(--fp-h);width:100%;height:calc(100% - var(--fp-h));
  object-fit:contain;background:#000;
  margin:auto;
  /* A CHANNEL CHANGE BETWEEN DIFFERENT RESOLUTIONS MOVES THE PICTURE BOX.
     It has to — the cap on enlargement is computed from the source, and the
     sources genuinely differ — but it should GLIDE rather than snap. A jump
     reads as a fault; an ease reads as the set adjusting, which is what it is.
     Short enough not to feel slow behind a 40ms channel change. */
  transition:max-width .22s ease, max-height .22s ease}
#screen.on{display:block}
/* Smooth for real video. The test pattern is a MEASUREMENT and gets
   pixelated instead — a smoothing filter hides the single-pixel defects it
   exists to expose. boot.js sets this per format. */
#screen.measure{image-rendering:pixelated}




/* ══ CHANNEL 0 — a guide CHANNEL ═══════════════════════════════════════
 * Not a web page with nice colours. A listings grid the way television did
 * it: warm grey ground, solid flat colour blocks, type sized to be read
 * across a room, and the grid filling the frame rather than floating in
 * whitespace. 80s/90s consumer-electronics and cel-animation palette,
 * executed with modern craft — crisp type, real spacing, no fake ageing.
 */
#guide{display:none;position:absolute;inset:0;overflow:hidden;
  background:var(--g-grey);color:var(--g-ink);font-family:var(--sans)}
#guide.on{display:flex;flex-direction:column}

.ep{position:absolute;inset:0;display:flex;flex-direction:column}

/* masthead — a solid band, big and calm */
.ep-top{display:flex;align-items:center;justify-content:space-between;gap:1em;
  background:var(--g-band);color:var(--g-cream);padding:1.6vh 2.6vw;flex:0 0 auto}
.ep-brand{display:flex;align-items:center;gap:.55em;
  font-size:clamp(21px,2.6vw,36px);font-weight:800;letter-spacing:.34em;
  color:var(--g-cream);text-transform:uppercase}
.ep-brand::before{content:"";width:.44em;height:.44em;background:var(--g-red);
  box-shadow:.7em 0 0 var(--g-yellow), 1.4em 0 0 var(--g-teal);margin-right:1.85em}
.ep-brand span{color:var(--g-yellow)}
.ep-brand em{display:none}
.ep-clock{font-family:var(--mono);font-variant-numeric:tabular-nums;font-weight:700;
  font-size:clamp(15px,1.7vw,25px);color:var(--g-cream);letter-spacing:.1em}

/* the grid FILLS what is left. No void underneath. */
.ep-grid{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden;
  /* The remote is fixed to the bottom of the screen and floats OVER whatever
     is behind it. On the guide that was the last channel's row, half hidden
     behind the buttons. A guide with a row you cannot read is missing a
     channel. */
  padding-bottom:64px}
.ep-ruler{flex:0 0 auto;display:grid;grid-template-columns:var(--chcol) 1fr;
  background:var(--g-band-2)}
.ep-chhead{padding:.55em 1.1em;font-size:10.5px;letter-spacing:.3em;
  color:var(--g-cream);font-weight:700;opacity:.75}
.ep-ruler .ep-track{position:relative;height:2.3em}
.ep-mark{position:absolute;top:.55em;padding-left:.7em;font-family:var(--mono);
  font-size:11.5px;color:var(--g-cream);font-variant-numeric:tabular-nums;
  border-left:2px solid rgba(255,255,255,.28);line-height:1.1;letter-spacing:.08em;
  font-weight:600}

/* every row shares the leftover height equally — the grid always fills */
.ep-row{flex:1 1 0;min-height:0;display:grid;grid-template-columns:var(--chcol) 1fr;
  border-top:1px solid rgba(143,162,189,.14)}
.ep-ch{display:flex;align-items:center;gap:.7em;padding:0 1.1em;
  background:var(--g-band);color:var(--g-cream)}
.ep-ch b{font-family:var(--mono);font-size:clamp(24px,3vw,42px);
  color:var(--g-yellow);min-width:1.15em;text-align:right;
  font-variant-numeric:tabular-nums;font-weight:700;line-height:1}
.ep-ch span{font-size:clamp(11px,1.15vw,15px);letter-spacing:.18em;
  text-transform:uppercase;font-weight:600;opacity:.92}
/* WHO IS WATCHING: an eye and its count, all of TOI atop the channel column
   and each channel in the top corner of its box (2026-11-08; the owner: "put it in top on that
   same column") — "<5" below the ratings' floor */
.ep-eye{display:inline-flex;align-items:center;gap:.45em;margin-left:auto;font-family:var(--mono);
  font-variant-numeric:tabular-nums;font-weight:700;letter-spacing:.06em;color:var(--g-cream)}
.ep-ch{position:relative}
.ep-ch .ep-eye{position:absolute;top:.55em;right:.7em;font-size:clamp(10px,1vw,13px);opacity:.92}
.ep-chhead{display:flex;align-items:center}
.ep-eye.all{font-size:clamp(11px,1.15vw,15px);letter-spacing:.06em;opacity:1}
.ep-eye svg{width:1.45em;height:.85em;fill:none;stroke:currentColor;stroke-width:1.6}
.ep-eye svg circle{fill:var(--g-yellow);stroke:none}
/* WHERE YOU ARE: the set's places across the top, and in each channel's box
   the level its programme comes from (2026-11-08) */
.ep-where{flex:0 0 auto;display:flex;align-items:center;gap:1em;padding:.45em 1.1em;background:var(--g-band-2);
  color:var(--g-cream);font-size:clamp(11px,1.15vw,15px);letter-spacing:.16em;text-transform:uppercase;font-weight:700}
.ep-where span{color:var(--g-yellow);letter-spacing:.24em;font-size:.85em}
.ep-ch > span:not(.ep-eye){min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ep-ch .ep-level{position:absolute;left:1.1em;bottom:.5em;font-style:normal;font-size:clamp(9px,.85vw,11px);
  letter-spacing:.18em;text-transform:uppercase;color:var(--g-yellow);opacity:.85}
.ep-track{position:relative;overflow:hidden;background:var(--g-grey-2)}

/* programmes are SOLID BLOCKS, cycling a small cel palette */
.ep-prog{position:absolute;top:3px;bottom:3px;left:0;
  background:var(--g-block);color:var(--g-cream);
  border-radius:3px;
  padding:.5em .8em;overflow:hidden;display:flex;flex-direction:column;
  justify-content:center;gap:.2em;border-right:3px solid var(--g-grey)}
.ep-row:nth-child(even) .ep-prog{background:var(--g-block-2)}
.ep-prog b{font-size:clamp(12px,1.25vw,18px);font-weight:700;letter-spacing:.01em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ep-prog i{font-style:normal;font-family:var(--mono);font-size:10.5px;
  font-variant-numeric:tabular-nums;color:var(--g-dim);font-weight:600}
.ep-prog.live{background:var(--g-block-2);color:var(--g-cream);
  box-shadow:inset 3px 0 0 var(--g-red)}
.ep-prog.live i{opacity:.85}
.ep-prog.strand{background:var(--g-block);color:var(--g-dim)}
.ep-nowbar{position:absolute;top:0;bottom:0;width:2px;background:var(--g-red);
  box-shadow:0 0 8px rgba(255,77,61,.55);z-index:3}

/* THE DETAIL BAND — what you are watching, above the grid. */
.ep-detail{flex:0 0 auto;display:flex;align-items:center;gap:1.4vw;
  min-height:calc(3vh + clamp(30px,4.2vw,58px));box-sizing:border-box;
  padding:1.5vh 2.6vw;background:linear-gradient(180deg,#12203a,#0d1626);
  border-bottom:1px solid rgba(143,162,189,.18)}
.ep-detail-num{font-family:var(--mono);font-weight:700;line-height:1;
  font-size:clamp(30px,4.2vw,58px);color:var(--g-yellow);
  font-variant-numeric:tabular-nums;min-width:1.4em;text-align:right}
.ep-detail-body{min-width:0;display:flex;flex-direction:column;gap:.35em}
.ep-detail-title{font-size:clamp(17px,2.3vw,34px);font-weight:700;
  color:var(--g-cream);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ep-detail-meta{display:flex;flex-wrap:nowrap;overflow:hidden;gap:.5em 1.1em;align-items:center;min-height:1.9em;
  font-size:clamp(10px,1.05vw,14px);letter-spacing:.14em;text-transform:uppercase;
  color:var(--g-dim);font-weight:600}
.ep-live.off{visibility:hidden}
.ep-live{color:#fff;background:var(--g-red);padding:.18em .6em;border-radius:2px;
  letter-spacing:.12em}

/* THE ROW YOU ARE ON. The one place colour means something. */
.ep-row.sel .ep-ch{background:var(--g-sel);color:#fff}
.ep-row.sel .ep-ch b{color:#fff}
.ep-prog.sel{background:var(--g-sel-2);color:#fff;
  box-shadow:inset 3px 0 0 var(--g-red)}
.ep-prog.sel i{color:rgba(255,255,255,.82)}

/* ══ CHANNEL BANNER ════════════════════════════════════════════════════ */
#banner{position:absolute;top:5vh;left:4vw;z-index:9;display:flex;align-items:baseline;
  gap:.65em;padding:.42em 1.05em;background:rgba(14,14,16,.9);
  border-left:5px solid var(--tv-amber);
  opacity:0;transform:translateY(-8px);transition:opacity .18s,transform .18s}
#banner.on{opacity:1;transform:none}
#banner b{font-family:var(--mono);font-size:clamp(30px,4vw,52px);line-height:1;
  color:var(--tv-cyan);font-weight:700;text-shadow:0 0 14px rgba(99,210,221,.45)}
#banner span{color:#efe9dc;font-size:clamp(12px,1.35vw,17px);letter-spacing:.2em;
  text-transform:uppercase}

/* ══ NOW/NEXT + POSITION BAR ═══════════════════════════════════════════ */
#info{position:absolute;left:0;right:0;bottom:0;z-index:6;padding:2.4vh 4vw 8.5vh;
  background:linear-gradient(transparent,rgba(12,11,9,.92) 42%);
  opacity:0;transform:translateY(10px);transition:opacity .22s,transform .22s;pointer-events:none}
#info.on{opacity:1;transform:none}
.nf-title{font-size:clamp(17px,2.2vw,30px);font-weight:600;color:var(--ink);letter-spacing:.01em}
.nf-sub{display:flex;gap:1.4em;flex-wrap:wrap;margin-top:.35em;
  font-family:var(--mono);font-size:clamp(10.5px,1.05vw,13.5px);color:var(--dim)}
.nf-sub .live{color:var(--amber)}
.nf-bar{margin-top:.9em;height:3px;background:rgba(242,237,227,.12);position:relative;overflow:hidden}
.nf-fill{position:absolute;inset:0 auto 0 0;background:var(--amber);
  box-shadow:0 0 8px rgba(255,178,58,.5)}
.nf-times{display:flex;justify-content:space-between;margin-top:.4em;
  font-family:var(--mono);font-size:11px;color:var(--faint);font-variant-numeric:tabular-nums}

/* ══ THE REMOTE ════════════════════════════════════════════════════════ */
/* THE SET'S FRONT PANEL (the owner's drawing, 2026-10-21): one strip of the
 * cabinet's dark plastic along the bottom of the TV, every key one shape with
 * its label under it, the channel in an amber window — shown as the bar was,
 * on hover, so the picture keeps the whole screen. */
#bar{position:absolute;bottom:0;left:0;right:0;z-index:7;display:flex;gap:14px;
  padding:10px max(16px,3vw) calc(10px + env(safe-area-inset-bottom,0px));
  justify-content:space-between;align-items:flex-end;flex-wrap:wrap;
  background:linear-gradient(180deg,var(--cab-2),var(--cab));
  box-shadow:0 -1px 0 rgba(255,255,255,.05),0 -8px 24px rgba(0,0,0,.35);
  opacity:0;transition:opacity .18s}
#tv:hover #bar,#bar:focus-within{opacity:1}
#bar .fp-brand{font:700 11px/1 var(--sans,system-ui);letter-spacing:.42em;color:var(--engrave);
  opacity:.75;padding-bottom:16px}
#bar .fp-keys{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap}
#bar .fp-k{display:flex;flex-direction:column;align-items:center;gap:5px}
#bar .fp-k label{font:600 9px/1 var(--sans,system-ui);letter-spacing:.2em;color:var(--engrave);
  text-transform:uppercase}
#bar .fp-row{display:flex;align-items:center;gap:6px}
#bar button{width:44px;height:30px;padding:0;border-radius:5px;border:1px solid #0b0a08;
  background:linear-gradient(180deg,var(--key-hi),var(--key));color:var(--engrave);cursor:pointer;
  box-shadow:0 2px 0 #0b0a08,inset 0 1px 0 rgba(255,255,255,.06);
  font:600 12px/1 var(--sans,system-ui);letter-spacing:.08em;text-transform:uppercase}
#bar button.wide{width:60px}
#bar button:active{transform:translateY(2px);box-shadow:0 0 0 #0b0a08}
#bar button:focus-visible{outline:2px solid var(--tv-cyan);outline-offset:2px}
#bar button.on{color:var(--tv-cyan);text-shadow:0 0 6px rgba(99,210,221,.7)}

/* the CH window: shows the channel you are ON, and accepts a new one */
#bar .chnum{min-width:42px;height:30px;display:flex;align-items:center;justify-content:center;
  background:#0b0907;border:1px solid #000;border-radius:4px}
#bar .chnum input{width:2.2em;background:transparent;border:0;outline:0;
  color:var(--tv-amber);font-family:var(--mono);font-size:18px;font-weight:600;
  font-variant-numeric:tabular-nums;text-align:center;padding:0;
  text-shadow:0 0 6px rgba(255,178,58,.6)}
#bar .chnum.typing{border-color:var(--red)}
#bar .chnum.typing input{color:var(--red)}

/* POWER: its lamp red while the transceiver says it is on */
#bar .fp-led{width:7px;height:7px;border-radius:50%;background:#3b1512;box-shadow:inset 0 0 2px #000}
#bar .fp-led.lit{background:#ff4a3a;box-shadow:0 0 6px #ff4a3a}
#bar button.pw{font-size:15px;color:#c86b5e}

/* OFF: dark glass, the panel up with only POWER live, nothing else on the
 * screen — the transceiver is off, so nothing arrives to show */
#tv.off #screen,#tv.off #guide,#tv.off #banner,#tv.off #info,#tv.off #osd,#tv.off #eq,
#tv.off #veq,#tv.off #entry,#tv.off #menu,#tv.off #mutemark,#tv.off #tell{visibility:hidden}
#tv.off #bar{opacity:1}
#tv.off #bar .fp-k:not(.fp-power){opacity:.35;pointer-events:none}
#tv.off #bar .chnum input{color:#3a2a18;text-shadow:none}

/* THE TUBE: off squeezes the picture to a bright line, then a dot; on opens
 * the dot to a line, the line to the picture, with a moment's bloom */
@keyframes tube-off{0%{transform:scale(1,1);filter:brightness(1);visibility:visible}
  55%{transform:scale(1,.006);filter:brightness(3);visibility:visible}
  80%{transform:scale(.004,.006);filter:brightness(5);visibility:visible}
  100%{transform:scale(0,0);filter:brightness(5);opacity:0;visibility:visible}}
@keyframes tube-on{0%{transform:scale(.004,.006);filter:brightness(5)}
  35%{transform:scale(1,.006);filter:brightness(3)}
  75%{transform:scale(1,1.02);filter:brightness(1.6)}
  100%{transform:scale(1,1);filter:brightness(1)}}
#tv.tube-off #screen{animation:tube-off .55s cubic-bezier(.5,0,.8,.3) forwards}
#tv.tube-on #screen{animation:tube-on .7s cubic-bezier(.2,.7,.3,1)}
@media (prefers-reduced-motion:reduce){#tv.tube-off #screen,#tv.tube-on #screen{animation-duration:.01s}}

/* THE ON-SCREEN MENU and MUTE, in the tube's cyan */
#menu{position:absolute;right:var(--pg-right);bottom:var(--pg-bottom);z-index:9;display:none;
  min-width:240px;box-sizing:border-box;padding:12px 14px;background:var(--pg);
  border:1px solid var(--pg-line);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.45);
  font:500 13.5px/1.2 var(--sans);color:var(--pg-ink)}
#menu.on{display:block}
#menu .mn-t{display:flex;justify-content:space-between;font-size:10.5px;font-weight:600;
  letter-spacing:.16em;color:var(--engrave);margin-bottom:8px}
#menu .mn-it{display:flex;justify-content:space-between;padding:7px 10px;cursor:pointer;
  border-radius:5px}
#menu .mn-it:hover{background:var(--pg-sel)}
#menu .mn-hint{font-size:11px;color:var(--engrave);margin-top:8px}
#mutemark{position:absolute;top:5vh;right:4vw;z-index:9;display:none;
  font:600 clamp(14px,1.6vw,20px)/1 var(--mono);letter-spacing:.18em;color:var(--tv-cyan);
  text-shadow:0 0 8px rgba(99,210,221,.8)}
#mutemark.on{display:block}

/* the big number while you type, like a TV OSD */
#entry{position:absolute;top:5vh;left:4vw;z-index:10;display:none;
  padding:.3em .85em;background:rgba(14,14,16,.9);border-left:5px solid var(--tv-amber);
  font-family:var(--mono);font-size:clamp(30px,4vw,52px);line-height:1.1;
  color:var(--tv-cyan);letter-spacing:.14em;font-variant-numeric:tabular-nums;
  font-weight:700;text-shadow:0 0 14px rgba(99,210,221,.45)}
#entry.on{display:block}

/* ══ INSTRUMENTATION ═══════════════════════════════════════════════════ */
#osd{position:absolute;right:var(--pg-right);bottom:var(--pg-bottom);z-index:7;display:none;
  background:var(--pg);border:1px solid var(--pg-line);border-radius:8px;
  box-shadow:0 8px 24px rgba(0,0,0,.45);padding:14px 16px;color:var(--pg-ink);
  font-family:var(--sans);font-size:12.5px;min-width:300px;
  max-height:calc(100vh - var(--fp-h) - 6vh);overflow:auto}
#osd.on{display:block}
/* The readings are meant to be copied out and pasted into a message, so they
 * select like text rather than like chrome. The rest of the television does
 * not — a set is not a document. */
#stats{user-select:text;-webkit-user-select:text;cursor:text}
#stats table{border-collapse:collapse;width:100%}
#stats td{padding:3px 0;border-bottom:1px solid var(--pg-sel)}
#stats td:first-child{color:var(--pg-soft);padding-right:1.8em;white-space:nowrap}
#stats td:last-child{text-align:right;color:var(--pg-ink);font-variant-numeric:tabular-nums}
#stats .hi{color:var(--pg-ink);font-weight:650}
#stats .ok{color:#8fbf9a} #stats .warn{color:var(--pg-amber)} #stats .bad{color:#e07a6f}

#status{position:absolute;bottom:11vh;left:4vw;z-index:6;font-family:var(--mono);
  font-size:11.5px;color:var(--panel);letter-spacing:.04em;
  background:rgba(12,11,9,.6);padding:.2em .6em}
#status.bad{color:var(--bad)}

/* audio readout — source layout, what it is downmixed to, and a live meter */
.nf-audio{display:flex;align-items:center;gap:1.1em;flex-wrap:wrap;margin-top:.7em;
  font-family:var(--mono);font-size:clamp(10px,1vw,12.5px);color:var(--dim);
  letter-spacing:.04em}
.nf-audio b{color:var(--gfaint);font-weight:600;letter-spacing:.22em;font-size:.92em}
.nf-audio.muted{color:var(--gfaint)}
.nf-fmt{color:var(--ink);font-weight:600}
.nf-fmt em{color:var(--red);font-style:normal;padding:0 .15em}
.nf-note{color:var(--gfaint)}
.nf-cta{color:#fff;background:var(--red);padding:.1em .55em;font-weight:600;letter-spacing:.1em}
.nf-meter{display:inline-flex;gap:2px;align-items:flex-end;height:12px;margin-left:auto}
.nf-meter i{width:3px;height:100%;background:rgba(246,240,226,.22);display:block}
.nf-meter i.on{background:var(--amber)}
.nf-meter i.hot{background:var(--red)}

/* ══ AUDIO / EQ PANEL ══════════════════════════════════════════════════ */
#eq{position:absolute;right:var(--pg-right);bottom:var(--pg-bottom);z-index:7;display:none;
  background:var(--pg);border:1px solid var(--pg-line);border-radius:8px;
  box-shadow:0 8px 24px rgba(0,0,0,.45);
  padding:14px 16px 12px;width:360px;box-sizing:border-box;
  font-family:var(--sans);font-size:12.5px;color:var(--pg-soft)}
#eq.on{display:block}
.eq-head{display:flex;align-items:flex-start;gap:.9em;margin-bottom:10px;
  padding-bottom:8px;border-bottom:1px solid var(--pg-line)}
.eq-head b{color:var(--pg-ink);letter-spacing:.06em;font-weight:650;font-size:14px}
.eq-head span{color:var(--pg-soft);font-size:11.5px;flex:1;min-height:2.6em;line-height:1.3}
.eq-none{color:var(--pg-soft);padding:6px 0 2px}

.eq-presets{display:flex;gap:5px;margin-bottom:14px;flex-wrap:wrap}
.eq-presets button{padding:.38em .75em;cursor:pointer;background:transparent;
  color:var(--pg-soft);border:1px solid var(--pg-line);border-radius:5px;font:inherit;
  font-size:12px}
.eq-presets button:hover{border-color:var(--engrave);color:var(--pg-ink)}
.eq-presets button.on{background:var(--pg-fill);border-color:var(--pg-fill);color:var(--cab);font-weight:600}
.eq-presets button[disabled]{opacity:.75;cursor:default}

.eq-bands{display:flex;gap:14px;justify-content:space-between;margin-bottom:14px}
.eq-band{display:flex;flex-direction:column;align-items:center;gap:6px}
.eq-db{color:var(--pg-ink);font-variant-numeric:tabular-nums;min-height:1.1em;font-size:12px}
.eq-hz{color:var(--pg-soft);font-size:11px}
#eq .eq-band input[type=range]{writing-mode:vertical-lr;direction:rtl;width:16px;height:92px}

.eq-rows label{display:flex;align-items:center;gap:.8em;margin-top:7px}
.eq-rows label span{flex:0 0 9.5em;color:var(--pg-soft)}
.eq-rows label b{flex:0 0 4em;text-align:right;color:var(--pg-ink);
  font-variant-numeric:tabular-nums;font-weight:500}
/* Night's reduction while it works: the one live reading among the controls */
.eq-rows b.live{color:var(--pg-amber)}
.eq-rows input[type=range]{flex:1}

/* THE PAGES' SLIDERS: a track and a knob in the set's colours, filled from
 * zero to the value (--z, --p: format.js fill), never the browser's own */
#eq input[type=range],#veq input[type=range]{-webkit-appearance:none;appearance:none;
  background:transparent;cursor:pointer;margin:0;height:16px}
#eq input[type=range]::-webkit-slider-runnable-track,#veq input[type=range]::-webkit-slider-runnable-track{
  height:6px;border-radius:3px;background:linear-gradient(to right,var(--pg-line) min(var(--z,0%),var(--p,0%)),
  var(--pg-fill) min(var(--z,0%),var(--p,0%)) max(var(--z,0%),var(--p,0%)),var(--pg-line) max(var(--z,0%),var(--p,0%)))}
#eq input[type=range]::-webkit-slider-thumb,#veq input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:12px;height:12px;margin-top:-3px;
  border-radius:50%;background:var(--pg-ink);box-shadow:0 1px 3px rgba(0,0,0,.6)}
#eq input[type=range]::-moz-range-track,#veq input[type=range]::-moz-range-track{
  height:6px;border-radius:3px;background:var(--pg-line)}
#eq input[type=range]::-moz-range-progress,#veq input[type=range]::-moz-range-progress{
  height:6px;border-radius:3px;background:var(--pg-fill)}
#eq input[type=range]::-moz-range-thumb,#veq input[type=range]::-moz-range-thumb{
  width:12px;height:12px;border:0;border-radius:50%;background:var(--pg-ink)}
/* the equaliser's bands stand up: the same track turned, filled from 0 dB */
#eq .eq-band input[type=range]::-webkit-slider-runnable-track{width:6px;height:100%;
  background:linear-gradient(to top,var(--pg-line) min(var(--z,0%),var(--p,0%)),
  var(--pg-fill) min(var(--z,0%),var(--p,0%)) max(var(--z,0%),var(--p,0%)),var(--pg-line) max(var(--z,0%),var(--p,0%)))}
#eq .eq-band input[type=range]::-webkit-slider-thumb{margin-top:0;margin-left:-3px}

/* A strand is continuous content, not a programme — flatter, no start time.
 *
 * This rule is HERE, after the palette block, and it used to paint cream
 * (--paper-2) from the old warm-grey guide. It quietly overrode the dark rule
 * above it, so on a dark grid exactly one block — channel 3 — came out light,
 * which reads as a highlight and means nothing. Two rules for one selector in
 * one stylesheet is a coin toss decided by line order. */
.ep-prog.strand{border-left-color:var(--g-dim);background:var(--g-block)}
.ep-prog.strand b{color:var(--g-dim)}
.ep-prog.strand i{font-style:italic;color:var(--g-dim)}

/* ══ TOUCH & SMALL SCREENS ═════════════════════════════════════════════
 * A phone is a television that happens to be small, not a desktop that got
 * narrow. Three things break without this, and the first is fatal:
 *
 *  1. The remote reveals on :hover, and TOUCH HAS NO HOVER — so on a phone
 *     the only control surface sat at 40% opacity and never came up. The app
 *     was unusable, not merely awkward.
 *  2. The listings grid is positioned by TIME across the width. At 390px a
 *     two-hour window puts a programme in 40 pixels. Narrow screens get the
 *     LIVE programme full-width instead — "what is on now", which is what a
 *     phone is actually asked.
 *  3. Tap targets. 12px buttons are a desktop affordance.
 */
@media (hover: none), (pointer: coarse) {
  /* No hover means no reveal — the remote is always up. */
  #bar { opacity: 1; }
  #bar button { padding: .72em 1.1em; font-size: 13px; }
  #bar .chnum input { font-size: 21px; }
}

@media (max-width: 820px) {
  /* Respect notches and home indicators. */
  #bar { padding: 10px max(3vw, env(safe-area-inset-left)) calc(10px + env(safe-area-inset-bottom)); 
         gap: 7px; flex-wrap: wrap; }
  #banner, #entry { top: max(3vh, env(safe-area-inset-top)); left: max(4vw, env(safe-area-inset-left)); }
  /* the one place, full width: the list and every page */
  :root { --pg-right: 3vw; }
  #menu, #eq, #veq, #osd { left: 3vw; width: auto; min-width: 0; }
  #osd { max-height: 52vh; font-size: 11px; }
  #info { padding: 2vh 4vw 13vh; }
  .ep-grid { padding-bottom: 92px; }

  /* The guide becomes a NOW list: one row per channel, the live programme
     full width. The time ruler means nothing at this width. */
  .ep-top { padding: 1.4vh 4vw; }
  .ep-brand { letter-spacing: .22em; }
  .ep-brand::before { margin-right: 1.35em; }
  .ep-ruler { display: none; }
  .ep-row { grid-template-columns: minmax(92px, 26vw) 1fr; }
  .ep-ch { padding: 0 .8em; }
  .ep-prog { display: none; }
  .ep-prog.live, .ep-prog.strand { display: flex; left: 0 !important; width: 100% !important;
                                   border-right: 0; }
  .ep-nowbar { display: none; }
}

/* Very small / portrait phones: the channel column shrinks again. */
@media (max-width: 480px) {
  .ep-row { grid-template-columns: minmax(74px, 30vw) 1fr; min-height: 62px; }
  .ep-ch b { font-size: 26px; }
  .ep-ch span { font-size: 10px; letter-spacing: .1em; }
  .ep-prog b { font-size: 13px; }
  #bar button { padding: .7em .9em; font-size: 12px; }
}

/* Landscape phone: the picture is the point — get the chrome out of the way. */
@media (max-height: 480px) and (orientation: landscape) {
  /* the panel 6 + key 30 + 5 + label 9 + 6: the picture ends where it begins */
  :root { --fp-h: 56px; }
  #bar { padding: 6px 3vw; }
  #banner b, #entry { font-size: 28px; }
  #info { padding: 1.5vh 3vw 9vh; }
}

/* The tuner's card: shown when there is nothing else to show, never over a
   running picture. A receiver that renders nothing is a receiver that looks
   broken, even when the fault is upstream and stated. */
#tell{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:8;
  display:none;padding:.8em 1.4em;background:rgba(14,14,16,.86);
  border-left:5px solid var(--tv-amber);color:#efe9dc;
  font-family:var(--mono);font-size:clamp(11px,1.35vw,17px);letter-spacing:.16em;
  text-align:center;max-width:80vw}
#tell.on{display:block}

/* THE CURSOR IN THE GUIDE: the row and programme it is on carry the strong
 * mark (above); a second click or Enter tunes there. */
.ep-prog { cursor: pointer; }
.ep-ch { cursor: pointer; }
/* Where the window is, when it is not now. */
.ep-off { font-family: var(--mono, monospace); font-size: 11px; letter-spacing: .12em;
  opacity: .7; margin-left: 14px; display: inline-block; width: 5ch; text-align: right; }

/* The guide's own time controls. Visible, because a control that exists only
 * as a keystroke is one most people never find. */
.ep-nav { display: flex; align-items: center; gap: 6px; margin-left: 16px; }
.ep-btn {
  background: rgba(255,255,255,.06); color: inherit;
  border: 1px solid rgba(255,255,255,.18); border-radius: 3px;
  padding: 5px 12px; cursor: pointer;
  font: inherit; font-size: 11px; letter-spacing: .16em;
}
.ep-btn:hover { background: rgba(255,255,255,.14); }
.ep-btn.on { background: rgba(255,255,255,.22); }

/* video EQ (picture) panel — mirrors #eq, on the right */
#veq{position:absolute;right:var(--pg-right);bottom:var(--pg-bottom);z-index:7;display:none;background:var(--pg);border:1px solid var(--pg-line);border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.45);padding:14px 16px 12px;width:384px;box-sizing:border-box;font-family:var(--sans);font-size:12.5px;color:var(--pg-soft)}
#veq.on{display:block}
/* One box for both pages: PICTURE's four sliders and COLOUR's looks, mixer,
 * white balance and tone sit in the same 384×408 panel, so turning the page
 * moves nothing. The head's right-hand button is the way between them. */
#veq{height:408px}
.eq-head button{margin-left:auto;background:none;border:0;padding:0;
  color:var(--pg-soft);font:inherit;font-size:12px;font-weight:600;
  cursor:pointer;white-space:nowrap}
.veq-crumb{flex:0 0 auto !important;min-height:0 !important;
  color:var(--pg-soft) !important;letter-spacing:.04em;font-size:12px}
.veq-grp{display:flex;align-items:baseline;justify-content:space-between;
  height:16px;margin:8px 0 5px;font-size:10.5px;letter-spacing:.16em;
  text-transform:uppercase;font-weight:600;color:var(--engrave)}
.veq-grp button{background:none;border:0;padding:0;color:var(--pg-soft);
  font:inherit;font-size:10.5px;letter-spacing:.12em;cursor:pointer}
/* the mixer's lamp: 8 px in the 16-px head, so lit or dark it moves nothing */
.veq-lamp{display:inline-block;width:8px;height:8px;border-radius:50%;
  border:1.5px solid var(--engrave);box-sizing:border-box;vertical-align:-1px;
  margin-right:7px}
.veq-lamp.on{background:var(--sage-deep);border-color:var(--sage-deep);
  box-shadow:0 0 5px var(--sage)}
.veq-chips{display:flex;gap:6px;align-items:center;height:22px;
  margin-bottom:8px}
.veq-chips button{width:22px;height:22px;border:2px solid transparent;
  background:var(--c);cursor:pointer;padding:0;box-sizing:border-box}
.veq-chips button.on{border-color:var(--pg-ink)}
.veq-chips span{margin-left:auto;font-size:10px;letter-spacing:.16em;
  color:var(--pg-soft);width:64px;text-align:right}
.veq-moved{font-size:11px;color:var(--pg-soft);height:14px;line-height:14px;
  margin:4px 0 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* NOTHING ON SCREEN CHANGES SIZE BECAUSE ITS CONTENT CHANGED (620438f, 2026-09-12).
 * A box is a property of the design, never of the value it shows: panels take
 * one width, a varying text sits in a slot sized for its longest value, a band
 * reserves its tallest line, a label that comes and goes is a slot that is
 * always laid out. tests/proofs/layout_holds.mjs measures every panel across
 * its states and fails on any rect that moves. */
.veq-rows{display:grid;gap:7px}
.veq-row{display:grid;grid-template-columns:78px 1fr 38px;align-items:center;gap:10px}
.veq-row label{color:var(--pg-soft);font-size:12px}
.veq-row input[type=range]{width:100%}
.veq-row span{text-align:right;color:var(--pg-ink);font-variant-numeric:tabular-nums}
/* after #veq's own width, or its 384 px wins on a narrow screen */
@media (max-width:820px){#veq{width:auto}}

/* ══ /ROI — radio's own page: the radio alone fills it ════════════════════
 * The owner's approved drawing (Set Design Review, 2026-10-30): the room, the
 * walnut radio, the lit dial 88 to 108 with a red needle, the grille, the red
 * light (on while the station's sound comes in), the plate, the cream keys
 * ▼ ▲ MUTE EQ. No knobs, no "FM". The AUDIO panel is the TV's own (#eq). */
body.roi{background:#0a0705}
body.roi #eq{top:6vh;right:4vw;bottom:auto}
#radio{position:fixed;inset:0;display:none}
#radio.on{display:block}
#radio .r-room{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  background:radial-gradient(70% 60% at 50% 42%,#2b2118 0,#17110c 60%,#0a0705 100%)}
#radio .r-radio{position:relative;width:min(84vw,150vh);aspect-ratio:2.05/1;
  border-radius:18px 18px 10px 10px;padding:4% 4.5% 3.5%;display:grid;grid-template-rows:42% 1fr;gap:5%;
  background:linear-gradient(180deg,#7a4a26 0,#5e361a 40%,#4a2a14 100%);
  box-shadow:0 18px 30px rgba(0,0,0,.6),inset 0 2px 0 rgba(255,220,170,.25),inset 0 -6px 12px rgba(0,0,0,.35)}
#radio .r-radio::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:repeating-linear-gradient(92deg,rgba(0,0,0,.05) 0 3px,rgba(255,255,255,.025) 3px 7px)}
#radio .r-brand{position:absolute;top:1.6%;left:50%;transform:translateX(-50%);
  font:italic 700 clamp(8px,1.1vw,13px)/1 Georgia,serif;letter-spacing:.3em;color:#e7c98f;opacity:.85}
#radio .r-dial{position:relative;border-radius:6px;overflow:hidden;background:linear-gradient(180deg,#f2deaa,#e3c47e);
  box-shadow:inset 0 0 0 2px #2c1a0c,inset 0 0 18px rgba(255,170,60,.55),0 0 22px rgba(255,170,60,.18)}
#radio .r-scale{position:absolute;left:5%;right:5%;top:12%;bottom:34%}
#radio .r-tick{position:absolute;bottom:0;width:1px;height:24%;background:#4a2f16}
#radio .r-tick.r-long{height:46%}
#radio .r-num{position:absolute;top:0;transform:translateX(-50%);font:700 clamp(9px,1.5vw,18px)/1 Georgia,"Times New Roman",serif;color:#3a2410}
#radio .r-needle{position:absolute;top:6%;bottom:6%;width:2px;margin-left:-1px;background:#c0221b;
  box-shadow:0 0 4px rgba(192,34,27,.6);transition:left .9s cubic-bezier(.4,.1,.2,1)}
#radio .r-low{display:grid;grid-template-columns:1fr 38%;gap:5%;min-height:0}
#radio .r-grille{border-radius:6px;background:repeating-linear-gradient(90deg,#2a1a0e 0 2px,transparent 2px 9%),
  repeating-linear-gradient(0deg,rgba(0,0,0,.18) 0 1px,transparent 1px 3px),linear-gradient(180deg,#b89a6a,#8f7448);
  box-shadow:inset 0 0 0 3px #2c1a0c,inset 0 0 14px rgba(0,0,0,.4)}
#radio .r-right{display:flex;flex-direction:column;justify-content:center;align-items:center;min-height:0;gap:7%}
#radio .r-lamp{width:11%;aspect-ratio:1;border-radius:50%;transition:background .2s,box-shadow .2s;
  background:radial-gradient(circle at 40% 35%,#5a2a22,#2a100c 70%);box-shadow:inset 0 0 0 2px #2c1a0c,0 0 0 3px #3a2410}
#radio .r-lamp.on{background:radial-gradient(circle at 40% 35%,#ffb3a0,#ff3b22 45%,#a3120a 100%);
  box-shadow:inset 0 0 0 2px #2c1a0c,0 0 0 3px #3a2410,0 0 14px 3px rgba(255,59,34,.65)}
#radio .r-plate{width:100%;text-align:center;background:#1d130b;border-radius:4px;box-shadow:inset 0 0 0 1px #6b4a26;
  padding:4%;color:#f0d9a4;font:700 clamp(9px,1.35vw,16px)/1.25 Georgia,serif;letter-spacing:.06em}
#radio .r-plate small{display:block;font:400 clamp(8px,1.1vw,13px)/1.3 Georgia,serif;letter-spacing:.02em;color:#cdb688;margin-top:2px}
#radio .r-keys{display:flex;gap:4%;justify-content:center;width:100%}
#radio .r-key{flex:1;max-width:24%;aspect-ratio:1.5/1;border:0;border-radius:3px 3px 5px 5px;cursor:pointer;
  background:linear-gradient(180deg,#f4e7c8,#d9c294 70%,#b39a68);box-shadow:0 3px 0 #4a2f16,inset 0 1px 0 rgba(255,255,255,.6);
  color:#3a2410;font:700 clamp(8px,1.1vw,13px)/1 Georgia,serif;letter-spacing:.06em}
#radio .r-key:active,#radio .r-key.down{transform:translateY(2px);box-shadow:0 1px 0 #4a2f16,inset 0 1px 0 rgba(255,255,255,.4)}

/* ══ /CB — the CB rig fills the page ════════════════════════════════════════
 * The owner's approved drawing (Set Design Review v118, 2026-11-03). The still
 * parts are one picture (src/cb/rig.svg); what moves and every printed legend
 * sit over it at the drawing's own coordinates, in units of the stage's width
 * (src/cb/rig_layout.js). Michroma ships with the page (OFL; src/cb/michroma)
 * — nothing is fetched from anyone else. */
@font-face{font-family:Michroma;src:url("src/cb/michroma/Michroma-Regular.ttf") format("truetype");font-display:block}
body.cb{margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#07080a}
.cb-stage{width:min(100vw,calc(100vh * 1000 / 390));container-type:inline-size}
.cb-rig{position:relative;width:100cqw;height:39cqw;overflow:hidden;user-select:none;touch-action:none;
  background:url("src/cb/rig.svg") 0 0/100% 100% no-repeat;font-family:Michroma,sans-serif}
.cb-rig>*{position:absolute}
.cb-legend{white-space:nowrap;line-height:1;letter-spacing:.19em;color:#c8ccd2;pointer-events:none}
.cb-legend.dim{color:#8a9097}
.cb-legend.brand{color:#e9ecef;font-style:italic;letter-spacing:.23em}
.cb-legend.tick{color:#dfe3e8;letter-spacing:0}
.cb-legend.meter{color:#2b1d0c;letter-spacing:.1em}
/* a channel or REACH level TOI has not opened yet: printed, greyed a little */
.cb-legend.shut{opacity:.38}
.cb-legend.hot{color:#c0241c;letter-spacing:.1em}
.cb-legend.reach{color:#cfd3d8;letter-spacing:.09em}
.cb-legend.reach.on{color:#ffb347}
.cb-digit{filter:drop-shadow(0 0 .2cqw rgba(255,59,34,.85))}
.cb-seg{position:absolute;inset:0}
.cb-seg.lit{background:#ff3b22}
.cb-turn{width:0;height:0;transform-origin:0 0;transition:transform .2s cubic-bezier(.3,1.4,.5,1)}
.cb-turn>*{position:absolute}
.cb-ptr{background:#f6f7f8}
.cb-ptr.ch{left:-.3cqw;top:-7cqw;width:.6cqw;height:3cqw;border-radius:.3cqw}
.cb-ptr.reach{left:-.22cqw;top:-3.3cqw;width:.44cqw;height:1.6cqw;border-radius:.2cqw}
.cb-ptr.vol{left:-.18cqw;top:-1.8cqw;width:.36cqw;height:1cqw;border-radius:.16cqw}
.cb-meter{overflow:hidden}
.cb-meter>*{position:absolute}
.cb-needle{left:-.11cqw;top:-10.15cqw;width:.21cqw;height:10.15cqw;background:#1a1a1a}
.cb-lamp{width:1.7cqw;height:1.7cqw;margin:-.85cqw 0 0 -.85cqw;border-radius:50%;opacity:0;transition:opacity .08s}
.cb-lamp.rx{color:#22c55e;background:radial-gradient(circle at 40% 35%,#9dffbc,#22c55e 50%,#0b5a26)}
.cb-lamp.tx{color:#ff3b22;background:radial-gradient(circle at 40% 35%,#ffc0b3,#ff3b22 50%,#7a0e06)}
.cb-lamp.busy{color:#ffa51f;background:radial-gradient(circle at 40% 35%,#ffe2a8,#ffa51f 50%,#7a4a06)}
.cb-lamp.lit{opacity:1;box-shadow:0 0 1.1cqw .2cqw currentColor}
.cb-ptt{left:-5.4cqw;top:-4cqw;width:1.6cqw;height:7.8cqw;box-sizing:border-box;border-radius:.5cqw;
  border:.15cqw solid #4a0b07;background:linear-gradient(90deg,#7d140d,#d83a2b 50%,#8f1a12);transition:transform .05s}
.cb-ptt.down{transform:translateX(.3cqw)}
.cb-hit{cursor:pointer}
.cb-hit.ch{left:9cqw;top:8.4cqw;width:16cqw;height:16cqw;border-radius:50%}
.cb-hit.reach{left:70.8cqw;top:9.6cqw;width:7.2cqw;height:7.2cqw;border-radius:50%}
.cb-hit.vol{left:72.3cqw;top:21.7cqw;width:4.2cqw;height:4.2cqw;border-radius:50%}
.cb-hit.ptt{left:-7cqw;top:-5cqw;width:4cqw;height:10cqw}
