@font-face {
  font-family: "DM Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("vendor/fonts/dm-mono-400-latin.woff2?v=e1896b13") format("woff2");
}
@font-face {
  font-family: "DM Mono"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("vendor/fonts/dm-mono-500-latin.woff2?v=9964608a") format("woff2");
}

/* Economic terminal — phosphor-on-glass. Square corners, mono
   everything, keyboard-first. Themes swap the accent phosphor only. */

:root {
  --bg: #070a0d;
  --bg2: #0a0e13;
  --panel: #0c1117;
  --panel2: #10161e;
  --border: #1b2634;
  --border2: #263447;
  --sat: 1;   /* text saturation multiplier — settings page */
  --lift: 0;  /* text lightness lift in % points — settings page */
  --text: hsl(207 calc(min(23 * var(--sat), 100) * 1%) calc(min(83 + var(--lift), 96) * 1%));
  --bright: hsl(204 calc(min(41 * var(--sat), 100) * 1%) calc(min(95 + var(--lift) * 0.5, 100) * 1%));
  /* Measured against --panel: --dim sat at 1.97:1 and --muted at 3.46:1, both
     below the 4.5:1 that body text needs and --dim below even the 3:1 floor
     for large text — on a page whose timestamps, placeholders, pager and panel
     notes are all --dim. Lifted to ~4.6:1 and ~6.3:1, keeping the hue, the
     saturation and the three-step ramp down from --text. */
  --muted: hsl(210 calc(min(14 * var(--sat), 100) * 1%) calc(min(54 + var(--lift), 74) * 1%));
  --dim: hsl(212 calc(min(19 * var(--sat), 100) * 1%) calc(min(50 + var(--lift), 70) * 1%));
  --gold: #d4af5a;      /* unique items — domain color, theme-invariant */
  --up: #2de08e;
  --down: #ff5c57;
  --warn: #e8b04f;
  --purple: #b57edc;
  --blue: #58a6ff;
  --acc: #2de08e;       /* phosphor */
  --acc-dim: rgba(45, 224, 142, .55);
  --acc-glow: rgba(45, 224, 142, .14);
  --acc-faint: rgba(45, 224, 142, .07);
  --row-border: rgba(27, 38, 52, .55);
}
:root[data-mode="light"] {
  --bg: #e9e7e0;
  --bg2: #e0ded6;
  --panel: #f4f2ea;
  --panel2: #eceae1;
  --border: #cbc8bc;
  --border2: #b1ada0;
  --text: hsl(210 calc(min(18 * var(--sat), 100) * 1%) calc(max(24 - var(--lift), 6) * 1%));
  --bright: hsl(210 calc(min(30 * var(--sat), 100) * 1%) calc(max(10 - var(--lift) * 0.5, 2) * 1%));
  --muted: hsl(208 calc(min(12 * var(--sat), 100) * 1%) calc(max(40 - var(--lift), 18) * 1%));
  --dim: hsl(206 calc(min(10 * var(--sat), 100) * 1%) calc(max(42 - var(--lift), 22) * 1%));
  --gold: #8a6a15;
  --up: #147d4e;
  --down: #c22823;
  --warn: #9c6a0a;
  --purple: #7b3fa6;
  --blue: #1d63c0;
  --acc: #0c8c5c;
  --acc-dim: rgba(12, 140, 92, .55);
  --acc-glow: rgba(12, 140, 92, .16);
  --acc-faint: rgba(12, 140, 92, .08);
  --row-border: rgba(120, 115, 105, .35);
}
:root[data-mode="light"][data-theme="amber"] {
  --acc: #a86400; --acc-dim: rgba(168,100,0,.55);
  --acc-glow: rgba(168,100,0,.15); --acc-faint: rgba(168,100,0,.08);
}
:root[data-mode="light"][data-theme="ice"] {
  --acc: #0d7cb0; --acc-dim: rgba(13,124,176,.55);
  --acc-glow: rgba(13,124,176,.15); --acc-faint: rgba(13,124,176,.08);
}
:root[data-mode="light"][data-theme="white"] {
  --text: hsl(0 0% calc(max(24 - var(--lift), 6) * 1%));
  --bright: hsl(0 0% calc(max(7 - var(--lift) * 0.5, 2) * 1%));
  --muted: hsl(0 0% calc(max(38 - var(--lift), 18) * 1%));
  --dim: hsl(0 0% calc(max(41 - var(--lift), 22) * 1%));
  --acc: #30343a; --acc-dim: rgba(48,52,58,.6);
  --acc-glow: rgba(48,52,58,.14); --acc-faint: rgba(48,52,58,.08);
}
:root[data-mode="light"] .brand .logo, :root[data-mode="light"] button.primary,
:root[data-mode="light"] .iv-btns button.on { color: #fff; }
:root[data-mode="light"] ::selection { background: var(--acc-dim); color: #fff; }

:root[data-theme="amber"] {
  --acc: #ffb347; --acc-dim: rgba(255,179,71,.55);
  --acc-glow: rgba(255,179,71,.13); --acc-faint: rgba(255,179,71,.06);
}
:root[data-theme="ice"] {
  --acc: #7dd3fc; --acc-dim: rgba(125,211,252,.55);
  --acc-glow: rgba(125,211,252,.13); --acc-faint: rgba(125,211,252,.06);
}
:root[data-theme="white"] {
  --text: hsl(0 0% calc(min(84 + var(--lift), 96) * 1%));
  --bright: hsl(0 0% calc(min(98 + var(--lift) * 0.25, 100) * 1%));
  --muted: hsl(0 0% calc(min(58 + var(--lift), 78) * 1%));
  --dim: hsl(0 0% calc(min(50 + var(--lift), 70) * 1%));
  --acc: #f4f7fb; --acc-dim: rgba(244,247,251,.68);
  --acc-glow: rgba(244,247,251,.16); --acc-faint: rgba(244,247,251,.07);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  background: var(--bg); color: var(--text);
  font-size: 13px; line-height: 1.45;
  font-family: var(--ui-font, "DM Mono", ui-monospace, monospace);
  font-variant-numeric: tabular-nums;
  overflow: hidden;
}
::selection { background: var(--acc-dim); color: #000; }
* { scrollbar-width: thin; scrollbar-color: var(--border2) transparent; }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--border2); }
::-webkit-scrollbar-track { background: transparent; }

#root, .frame { height: 100%; min-width: 0; }
.frame { display: flex; flex-direction: column; max-width: 100%; overflow: hidden; }

/* CRT stack — all knobs are CSS variables written by the settings page.
   --crt-line: scanline alpha · --crt-gap: pitch · --crt-vig: vignette alpha
   --ab-shadow: rgb-bleed + bloom text-shadow (inherits to every glyph) */
.crt::before {
  content: ""; position: fixed; inset: 0; z-index: 90; pointer-events: none;
  background: repeating-linear-gradient(0deg,
    rgba(0, 0, 0, var(--crt-line, .11)) 0 1px, transparent 1px var(--crt-gap, 3px));
}
.crt::after {
  content: ""; position: fixed; inset: 0; z-index: 91; pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 62%, rgba(0, 0, 0, var(--crt-vig, .32)) 100%);
}
.frame { text-shadow: var(--ab-shadow, none); }
body.crt-flicker.crt::before { animation: crtflick 4.2s steps(1) infinite; }
@keyframes crtflick {
  0%, 100% { opacity: 1; } 7% { opacity: .82; } 8% { opacity: 1; }
  31% { opacity: .9; } 33% { opacity: 1; } 61% { opacity: .85; } 62% { opacity: 1; }
}
.rollbar { display: none; }
body.crt-roll .rollbar {
  display: block; position: fixed; left: 0; right: 0; top: 0; height: 120px;
  z-index: 92; pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(255, 255, 255, .03), transparent);
  animation: crtroll 8s linear infinite;
}
@keyframes crtroll { from { transform: translateY(-20vh); } to { transform: translateY(115vh); } }

a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; }
a:focus-visible { outline: 1px solid var(--acc); outline-offset: 2px; }
a.unique, .unique { color: var(--gold); }
/* A control that reads as a link because it goes somewhere, but is a button
   because it changes state on the way — the league directory switches league
   and then navigates, which no href can express. */
button.linkish {
  background: none; border: 0; padding: 0; color: var(--blue);
  font: inherit; text-transform: none; letter-spacing: inherit; cursor: pointer;
}
button.linkish:hover { text-decoration: underline; }
.mono { font-family: inherit; }
.muted { color: var(--muted); }
.dim { color: var(--dim); }
.up { color: var(--up); }
.down { color: var(--down); }
.acc { color: var(--acc); }
/* A ratio built on thin support: keeps its direction colour, wears a dotted
   rule to say the number is held loosely. */
.thin { border-bottom: 1px dotted currentColor; }
.err { color: var(--down); padding: 14px; }
.loading { color: var(--muted); padding: 24px 4px; }
.loading::after { content: "▊"; color: var(--acc); animation: blink 1s steps(1) infinite; margin-left: 4px; }
@keyframes blink { 50% { opacity: 0; } }

kbd {
  display: inline-block; padding: 0 6px; margin: 0 1px;
  border: 1px solid var(--border2); border-bottom-width: 2px;
  color: var(--bright); background: var(--panel2); font: inherit; font-size: 11px;
  line-height: 18px;
}

/* ---------- inline help ----------
   A hint is a link that happens to show a sentence on hover. The bubble is a
   CHILD of the link, so hovering it keeps it open and clicking it opens the
   topic; the wrapper's transparent padding bridges the gap to the marker so
   the pointer never crosses dead space on the way. */
.hint {
  color: var(--acc-dim); text-decoration: none; cursor: help;
  position: relative; white-space: nowrap;
}
.hint:hover, .hint:focus-visible { color: var(--acc); text-decoration: none; }
/* The marker is a mark ON the line, not a character IN it: it centres on the
   middle of the text it annotates instead of sitting on its baseline, and it
   scales with that text so a 10px uppercase stat label and 13px body copy get
   proportionate boxes. The anchor carries the alignment because the bubble is
   fixed-positioned and therefore never a flex item. */
a.hint:not(.hint-term) {
  display: inline-flex; align-items: center; vertical-align: middle;
  /* Uppercase contexts already get air from their letter-spacing; running
     text gets none, so the gap is set for the tighter case. */
  margin: 0 .25em;
}
.hint-mark {
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle; box-sizing: content-box;
  width: 1em; height: 1em; padding: .1em;
  font-size: max(8px, .72em); font-weight: 700; line-height: 1;
  border: 1px solid currentColor;
  /* Labels, panel rails and table headers all set letter-spacing, which adds
     a trailing gap AFTER the glyph and pushes it left of centre inside the
     box. The mark is one glyph; it wants none of that. */
  letter-spacing: 0; text-transform: none; text-indent: 0; font-style: normal;
}
/* `vertical-align: middle` targets baseline + half x-height, which is the
   optical centre of running text — measured within 0.3px in .sub, td and
   .muted. All-caps labels have no x-height to centre on and caps are taller,
   so the same rule lands the mark ~1px low; lift it to the cap centre.
   .ptitle needs its own value because it centres children with flex
   align-items rather than vertical-align, and its 10px gap would otherwise
   leave the mark floating between the title and its note. */
.stat .k > .hint, th > .hint { position: relative; top: -.1em; }
.ptitle > .hint { position: relative; top: -.13em; margin-left: -6px; }
/* the labelled form: the term itself is the target */
.hint-term { color: inherit; border-bottom: 1px dotted var(--acc-dim); }
.hint-term:hover, .hint-term:focus-visible { color: var(--acc); border-bottom-color: var(--acc); }
.hint-pop {
  position: fixed; z-index: 95; padding: 7px 0; text-shadow: none;
  pointer-events: auto; white-space: normal;
}
.hint-pop.above { transform: translateY(-100%); }
.hint-body {
  display: block; background: var(--panel2); border: 1px solid var(--acc-dim);
  box-shadow: 0 6px 26px -8px rgba(0,0,0,.75); padding: 7px 10px 6px;
  color: var(--text); font-size: 11.5px; line-height: 1.45; letter-spacing: 0;
  text-transform: none; font-weight: 400;
}
.hint-body b {
  display: block; color: var(--acc); font-size: 10px; font-weight: 600;
  letter-spacing: 1.4px; text-transform: uppercase; margin-bottom: 2px;
}
.hint-body i {
  display: block; color: var(--blue); font-style: normal; font-size: 10.5px;
  margin-top: 4px;
}
.hint:hover .hint-body { border-color: var(--acc); }

/* ---------- help page ---------- */
.help-page { display: grid; grid-template-columns: 216px minmax(0, 1fr); gap: 26px; align-items: start; }
.help-toc { position: sticky; top: 0; max-height: calc(100vh - 160px); overflow-y: auto; padding-bottom: 10px; }
.help-toc-sec { margin-bottom: 14px; }
.help-toc-t {
  color: var(--muted); font-size: 10px; letter-spacing: 1.6px; text-transform: uppercase;
  border-bottom: 1px solid var(--border); padding-bottom: 3px; margin-bottom: 4px;
}
.help-toc-l {
  display: block; color: var(--muted); font-size: 12px; padding: 1px 0 1px 8px;
  border-left: 1px solid transparent; overflow: hidden; text-overflow: ellipsis;
}
.help-toc-l:hover { color: var(--text); text-decoration: none; border-left-color: var(--border2); }
.help-toc-l.on { color: var(--acc); border-left-color: var(--acc); }
.help-body { max-width: 78ch; }
.help-body section { margin-bottom: 8px; }
.help-body .ptitle { margin-top: 20px; }
.help-topic {
  border-left: 1px solid var(--border); padding: 2px 0 2px 14px; margin: 0 0 18px;
  scroll-margin-top: 12px;
}
.help-topic h3 {
  margin: 0; color: var(--bright); font-size: 13px; font-weight: 600; letter-spacing: .6px;
}
.help-topic .help-short { color: var(--acc-dim); font-size: 11.5px; margin: 1px 0 7px; }
.help-topic p { margin: 0 0 8px; color: var(--text); font-size: 12.5px; line-height: 1.55; }
.help-topic .help-list { margin: 0 0 8px; padding-left: 16px; color: var(--text); font-size: 12.5px; line-height: 1.55; }
.help-topic .help-list li { margin-bottom: 3px; }
.help-topic em { color: var(--muted); font-style: italic; }
.help-topic .help-also { color: var(--dim); font-size: 11px; margin-top: 6px; }
.help-topic .help-also a { margin-right: 8px; }
.help-topic.target { border-left-color: var(--acc); background: var(--acc-faint); }
.help-topic.target h3 { color: var(--acc); }
@media (max-width: 900px) {
  .help-page { grid-template-columns: 1fr; gap: 14px; }
  .help-toc { position: static; max-height: none; }
  details.help-toc > summary {
    cursor: pointer; color: var(--acc); font-size: 11px; letter-spacing: 2px;
    text-transform: uppercase; padding: 6px 0; list-style: none;
  }
  details.help-toc > summary::before { content: "▸ "; }
  details.help-toc[open] > summary::before { content: "▾ "; }
  details.help-toc .help-toc-l { padding: 7px 0 7px 9px; }
}

/* ---------- top: ticker tape ---------- */
.tape {
  height: 26px; overflow: hidden; white-space: nowrap; position: relative;
  background: var(--bg2); border-bottom: 1px solid var(--border);
  user-select: none; flex: none;
}
.tape-inner {
  display: flex; width: max-content; line-height: 26px;
  animation: tapescroll 55s linear infinite;
}
.tape:hover .tape-inner { animation-play-state: paused; }
@keyframes tapescroll { to { transform: translateX(-50%); } }
.tape-half { display: flex; padding-right: 34px; }
.tape .ti { margin-right: 34px; font-size: 12px; color: var(--muted); }
.tape .ti b { color: var(--bright); font-weight: 500; }
.tape .ti .sym { color: var(--acc); }

/* ---------- header ---------- */
.topbar {
  display: flex; align-items: stretch; height: 42px; flex: none;
  background: var(--panel); border-bottom: 1px solid var(--border);
  padding: 0 14px; gap: 4px; min-width: 0; overflow: hidden;
}
.brand {
  display: flex; align-items: center; gap: 9px; margin-right: 18px;
  color: var(--acc); font-weight: 700; letter-spacing: 2px; font-size: 14px;
  cursor: pointer; user-select: none; flex: none; padding: 0; border: 0;
  background: none; text-transform: none; font-family: inherit;
}
.brand .nb {
  display: block; max-width: 240px; overflow: hidden; white-space: nowrap;
  text-overflow: ellipsis;
}
.brand .logo {
  background: var(--acc); color: #000; padding: 1px 7px; font-weight: 800;
  text-shadow: none; letter-spacing: 0;
}
.tabs {
  display: flex; align-items: stretch; min-width: 0; overflow-x: auto;
  overscroll-behavior-x: contain; scrollbar-width: none;
  /* Same content-anchored/box-anchored pair as .pscroll: on a phone only a
     couple of the ten tabs fit, and with the scrollbar hidden there was
     nothing to say the rest of the navigation existed. */
  background:
    linear-gradient(to right, var(--panel), var(--panel)) left center / 14px 100% no-repeat local,
    linear-gradient(to left, var(--panel), var(--panel)) right center / 14px 100% no-repeat local,
    linear-gradient(to right, var(--acc-dim) 0 2px, transparent 2px) left center / 14px 100% no-repeat scroll,
    linear-gradient(to left, var(--acc-dim) 0 2px, transparent 2px) right center / 14px 100% no-repeat scroll;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  display: flex; align-items: center; padding: 0 13px; cursor: pointer; flex: none;
  color: var(--muted); font-size: 12px; letter-spacing: 1px; border: 0;
  background: none; font-family: inherit; border-bottom: 2px solid transparent;
}
.tab i { font-style: normal; color: var(--dim); margin-right: 6px; font-size: 10px; }
.tab:hover { color: var(--text); background: var(--acc-faint); }
.tab.on { color: var(--acc); border-bottom-color: var(--acc); background: var(--acc-faint); }
.tab.on i { color: var(--acc-dim); }
.topbar .sp { flex: 1; }
.league-chip {
  align-self: center; color: var(--gold); font-size: 12px; cursor: pointer;
  border: 1px solid var(--border2); padding: 3px 10px; user-select: none; flex: none;
  background: var(--panel2); text-transform: none; letter-spacing: 0;
}
.league-chip:hover { border-color: var(--gold); }
.league-chip i { font-style: normal; color: var(--dim); margin-right: 6px; }

/* ---------- main viewport ---------- */
main {
  flex: 1; overflow-y: auto; overflow-x: hidden;
  padding: 16px 18px 26px; scroll-padding: 60px;
}
.wrap { max-width: 1560px; margin: 0 auto; }

.page-title { margin: 2px 0 14px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.page-title h1 {
  font-size: 16px; margin: 0; color: var(--bright); font-weight: 600; letter-spacing: 1px;
  white-space: nowrap;
}
.page-title .sub { color: var(--muted); font-size: 12px; min-width: 0; }
.page-title .spacer { flex: 1; }
.page-title img { image-rendering: auto; }

/* panels with ── TITLE ────── rails */
.panel {
  background: var(--panel); border: 1px solid var(--border);
  padding: 10px 12px 12px; margin-bottom: 14px; min-width: 0;
}
/* Scrolling moved off .panel and onto this inner box so the panel title stays
   put and the scrollable edge can be marked. The two `local` layers are
   anchored to the CONTENT and the two `scroll` layers to the BOX, so each
   shadow is covered by its own patch of panel colour exactly while that end
   is fully in view: the cue appears only in the direction there is more to
   see, and needs no JS to know when. `--panel` must be opaque for the covers
   to hide the shadows; every theme's panel colour is. */
.pscroll {
  /* `contain` on the axis, or swiping a wide table past its last column hands
     the gesture to the browser and navigates back out of the page. */
  overflow-x: auto; min-width: 0; overscroll-behavior-x: contain;
  background:
    linear-gradient(to right, var(--panel), var(--panel)) left center / 18px 100% no-repeat local,
    linear-gradient(to left, var(--panel), var(--panel)) right center / 18px 100% no-repeat local,
    linear-gradient(to right, var(--acc-dim) 0 2px, transparent 2px) left center / 18px 100% no-repeat scroll,
    linear-gradient(to left, var(--acc-dim) 0 2px, transparent 2px) right center / 18px 100% no-repeat scroll,
    linear-gradient(to right, var(--acc-glow), transparent) left center / 18px 100% no-repeat scroll,
    linear-gradient(to left, var(--acc-glow), transparent) right center / 18px 100% no-repeat scroll,
    var(--panel);
  scrollbar-color: var(--acc-dim) var(--bg2);
}
.pscroll::-webkit-scrollbar-thumb { background: var(--acc-dim); }
.pscroll::-webkit-scrollbar-track { background: var(--bg2); }
/* A scrolled panel needs its bar to sit clear of the last row of figures. */
.pscroll:hover { scrollbar-color: var(--acc) var(--bg2); }
.panel.focus { border-color: var(--acc-dim); box-shadow: 0 0 0 1px var(--acc-faint), 0 0 22px -6px var(--acc-glow) inset; }
.ptitle {
  display: flex; align-items: center; gap: 10px; margin: 0 0 8px;
  color: var(--acc); font-size: 11px; font-weight: 600; letter-spacing: 2px;
  text-transform: uppercase; white-space: nowrap;
}
.ptitle::before { content: "──"; color: var(--border2); }
.ptitle::after { content: ""; flex: 1; border-top: 1px solid var(--border); }
.ptitle .pnote { color: var(--dim); font-weight: 400; letter-spacing: .4px; text-transform: none; }

.cols { display: grid; gap: 14px; grid-template-columns: 1fr 1fr; align-items: start; }
.cols > .panel { margin-bottom: 0; }
/* A row holding one panel is not a two-column row. Left to the template it
   took half the width and left the other half blank — the market page's
   commodity tape squeezed its four columns into 780px beside nothing. */
.cols > .panel:only-child { grid-column: 1 / -1; }
.cols { margin-bottom: 14px; }
@media (max-width: 1000px) { .cols { grid-template-columns: 1fr; } }

/* stat blocks.
   auto-FIT, not auto-fill: auto-fill holds empty tracks open, so a six-tile
   row stopped dead three quarters across the page and a ninth tile landed
   alone on a second row at an eighth of the width. Fitting collapses the
   unused tracks and lets the real tiles take the space. The 240px ceiling
   152px floor is what puts nine of them on one row at full width; the max
   stays flexible because a fixed one is what auto-fit counts tracks by, and
   capping it at 240px cut the row back to six and re-orphaned the ninth. */
.stats { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(152px, 1fr)); margin-bottom: 14px; }
.stat {
  background: var(--panel); border: 1px solid var(--border); padding: 8px 11px 9px;
  position: relative; overflow: hidden;
}
/* The label row is reserved, so a tile whose label wraps to two lines does not
   push its figure below the figures beside it — a row of stats that reads as a
   row only when every label happens to be short is not a row. */
.stat .k {
  color: var(--muted); font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase;
  min-height: 2.4em;
}
.stat .v { color: var(--bright); font-size: 19px; margin-top: 3px; font-weight: 600; }
.stat .v small { color: var(--muted); font-size: 11px; font-weight: 400; }
.stat svg { position: absolute; right: 6px; bottom: 6px; opacity: .8; }

/* tables */
table { border-collapse: collapse; width: 100%; font-size: 12.5px; }
th {
  text-align: left; color: var(--muted); font-weight: 500; font-size: 10px;
  letter-spacing: 1.2px; text-transform: uppercase; padding: 3px 9px 5px;
  border-bottom: 1px solid var(--border2); white-space: nowrap;
}
td { padding: 3px 9px; border-bottom: 1px solid var(--row-border); white-space: nowrap; vertical-align: middle; }
td.wrap { white-space: normal; }
th.num, td.num { text-align: right; }
/* Sortable headers. The arrow is always present, not only on the active
   column: a marker that appears on hover is a marker nobody knows to look
   for, and on a touch screen there is no hover to reveal it. */
th.sortable { user-select: none; white-space: nowrap; padding: 0; }
th.sortable .thsort {
  background: none; border: 0; padding: 3px 9px 5px; color: inherit;
  font: inherit; font-size: inherit; letter-spacing: inherit;
  text-transform: inherit; cursor: pointer; width: 100%; text-align: inherit;
}
th.sortable.num .thsort { text-align: right; }
th.sortable:hover, th.sortable:focus-within { color: var(--acc); }
th.sortable.sorted { color: var(--acc); }
.sarrow { font-style: normal; font-size: 8px; margin-left: 5px; color: var(--dim); vertical-align: 1px; }
th.sortable.sorted .sarrow { color: var(--acc); }
th.sortable:hover .sarrow { color: var(--acc-dim); }

.pager {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 9px 2px 1px; margin-top: 4px; border-top: 1px solid var(--border);
  font-size: 11.5px;
}
.pager .chip { font-size: 11px; }
.pager kbd { font-size: 10px; line-height: 15px; padding: 0 4px; }

tr.row { cursor: pointer; }
tr.row:hover td { background: var(--acc-faint); }
tr.current td { background: var(--acc-faint); color: var(--bright); }
tr.current td:first-child { box-shadow: inset 2px 0 0 var(--gold); }
tr.sel td { background: var(--acc-glow) !important; color: var(--bright); }
tr.sel td:first-child { box-shadow: inset 2px 0 0 var(--acc); }
tr.flash td { animation: rowflash 1.4s ease-out; }
@keyframes rowflash { from { background: var(--acc-glow); } }

/* roll-vs-price sparklines in the modifiers-by-impact table */
svg.spark { display: block; }
svg.spark polyline { fill: none; stroke-width: 1.5; }
svg.spark .sp-clean { stroke: var(--gold); }
svg.spark .sp-corr { stroke: var(--down); opacity: .65; }
svg.spark .sp-vol { fill: var(--warn); }

/* roll-value grid nested inside the modifiers table */
/* Directory tables: the same three columns listed on one page three times
   over. Left to auto layout each table sized its own columns to its own
   longest string, so the three grids disagreed by 200 px and the page read as
   three unrelated blocks. Fixed proportions make them one table with rules
   through it. Prose wraps rather than truncating — at phone width the
   descriptions were being cut mid-word. */
table.idx { table-layout: fixed; }
table.idx th, table.idx td { white-space: normal; vertical-align: top; }
table.idx th:first-child, table.idx td:first-child { width: 23%; }
table.idx th:nth-child(2), table.idx td:nth-child(2) { width: 37%; }

table.rvt { width: auto; font-size: 11.5px; margin: 2px 0; }
table.rvt th, table.rvt td { padding: 1px 8px 1px 0; border-bottom: none; }
table.rvt th { font-size: 9px; letter-spacing: .6px; padding-bottom: 2px; }
table.rvt th .b { font-size: 9px; margin-left: 2px; }
table.rvt td.lbl { color: var(--muted); font-size: 9.5px; text-transform: uppercase; letter-spacing: .6px; padding-right: 6px; }
table.rvt td.rvx { color: var(--muted); font-size: 10.5px; text-align: left; }
table.modsec { margin-bottom: 14px; }
table.modsec td { vertical-align: top; padding-top: 5px; }

/* text badges — terminal style */
.b { font-size: 10.5px; letter-spacing: .5px; margin-left: 5px; font-weight: 600; }
.b.corr { color: var(--down); }
.b.foul { color: var(--purple); }
.b.vest { color: var(--blue); }
.b.foil { color: var(--gold); }
.b.sold { color: var(--up); }
.b.vol { color: var(--warn); }
.b.plain { color: var(--muted); font-weight: 400; }
/* Not a verdict: the lookup behind it did not complete. Warn-coloured so it
   never reads as the quiet negative it replaced. */
.b.warnb { color: var(--warn); }
.b.trusted { color: var(--up); }
.b.saleonly { color: var(--blue); }
.b.ignored { color: var(--warn); }

/* Evidence colors. Sale-derived prices stay phosphor/gold; asking prices are
   blue everywhere so a listed price can never be mistaken for a sold one at a
   glance, whatever column it lands in. --blue has a dark and a light value,
   so this tracks the theme like the trusted/sale-only policy badges do. */
.askv { color: var(--blue); }
.evidence { color: var(--muted); font-size: 11px; margin: 6px 0 0; }
.evidence .sep { opacity: .45; margin: 0 6px; }

/* forms */
input[type=text], input[type=number], input[type=password], textarea, select {
  background: var(--bg2); color: var(--bright); border: 1px solid var(--border2);
  padding: 6px 9px; font: inherit; caret-color: var(--acc); min-width: 0;
}
::placeholder { color: var(--dim); opacity: 1; }
input:focus, textarea:focus, select:focus, button:focus-visible, tr:focus-visible {
  outline: 1px solid var(--acc); outline-offset: -1px;
}
/* A modifier line is about thirty characters. Left at width:100% this was a
   1530px box to type "+13% increased Spell Damage" into, which reads as a
   document editor rather than a field — and put the estimate button a full
   screen-width from the text it acts on. */
textarea { width: 100%; max-width: 720px; min-height: 96px; resize: vertical; }
button {
  background: var(--panel2); color: var(--acc); border: 1px solid var(--border2);
  padding: 6px 14px; font: inherit; font-size: 12px; letter-spacing: 1px;
  cursor: pointer; text-transform: uppercase;
}
button:hover { border-color: var(--acc-dim); background: var(--acc-faint); }
button:disabled { cursor: not-allowed; opacity: .45; }
button:disabled:hover { border-color: var(--border2); background: var(--panel2); filter: none; }
button.primary { background: var(--acc); color: #000; border-color: var(--acc); font-weight: 700; }
button.primary:hover { filter: brightness(1.12); }
.frow { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.frow label { color: var(--muted); font-size: 12px; display: flex; gap: 6px; align-items: center; }

.iv-btns button { padding: 3px 10px; font-size: 11px; }
.iv-btns button.on { color: #000; background: var(--acc); border-color: var(--acc); font-weight: 700; }

.estimate { font-size: 24px; color: var(--acc); font-weight: 700; margin: 10px 0 2px; }
.estimate small { display: block; font-size: 11.5px; color: var(--muted); font-weight: 400; margin-top: 3px; }
.note {
  border-left: 2px solid var(--warn); background: var(--panel2);
  color: var(--muted); padding: 6px 10px; font-size: 12px; margin: 8px 0;
}
.card-flavour {
  color: var(--muted); white-space: pre-line; margin: 8px 0;
  font-style: italic;
}
.card-flavour span { color: var(--dim); font-style: normal; }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: 2px 14px; font-size: 12.5px; }
.kv .k { color: var(--muted); }

.chart-box { position: relative; height: 250px; }
.chart-box.tall { height: 330px; }
/* A chart's absence, drawn at the chart's size — see NoChart in app.js. */
.chart-box.empty {
  display: flex; align-items: center; justify-content: center;
  border: 1px dashed var(--border); font-size: 12px;
}

/* complete retained state for a single observed item */
.item-state-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.item-state .kv { align-content: start; }
.item-state .kv > div { min-width: 0; }
.item-state .break { overflow-wrap: anywhere; white-space: normal; }
.item-mods { margin-top: 2px; }
.item-mods td { vertical-align: top; }
@media (max-width: 1000px) { .item-state-cols { grid-template-columns: 1fr; } }

/* lifecycle timeline */
.timeline { list-style: none; margin: 0; padding: 0; font-size: 12.5px; }
.timeline li { padding: 4px 0 4px 16px; border-left: 1px solid var(--border2); position: relative; }
.timeline li::before {
  content: "▪"; position: absolute; left: -4px; top: 3px; color: var(--blue); background: var(--panel);
}
.timeline li.ev-listed::before { color: var(--up); }
.timeline li.ev-removed::before { color: var(--down); }
.timeline li.ev-price_changed::before { color: var(--warn); }

/* phosphor-mask item art: accent-filled alpha mask + rgb ghost copies.
   The final layer can restore the source image's colors without weakening
   the binary silhouette/cut: it is clipped by the exact same CSS mask. */
.crticon { position: relative; display: inline-block; vertical-align: middle;
  margin-right: 7px; line-height: 0; }
.crticon img { display: block; }
.crticon.processed canvas { display: block; }
.crticon i { display: none; position: absolute; inset: 0; pointer-events: none;
  background: var(--acc);
  -webkit-mask: var(--icon-url) center / contain no-repeat;
  mask: var(--icon-url) center / contain no-repeat; }
/* GLOBAL mode rules apply only to unforced icons: forced preview cells
   (.labon/.labnat — settings strip) are exempt so every cell
   renders its own kind truthfully whatever mode the site is in. */
body.icoph .crticon:not(.labon):not(.labnat) img { opacity: 0; }
body.icoph .crticon:not(.labon):not(.labnat) i { display: block; }
/* engraved: EVERY layer (ghosts too) is cut by the sobel mask, so cuts
   punch through to the page background; the offset ghosts survive only
   as thin misregistration fringes along each stroke's edges */
body.icocut .crticon:not(.labon):not(.labnat) i,
.crticon.labcut i {
  -webkit-mask-image: var(--icon-cut, var(--icon-url));
  mask-image: var(--icon-cut, var(--icon-url));
}
/* forced-on mask variants */
.crticon.labon img { opacity: 0; }
.crticon.labon i { display: block; }
.crticon i.c { background: #00e5ff; opacity: .7;
  transform: translateX(calc(0px - var(--ico-ab, 1.5px))); }
.crticon i.r { background: #ff2d46; opacity: .7;
  transform: translateX(var(--ico-ab, 1.5px)); }
.crticon i.o {
  background: var(--icon-color) center / contain no-repeat;
  opacity: var(--ico-color, 0);
}

/* source-configured splash */
.cols > div { min-width: 0; }
.logo-line {
  text-align: center; line-height: 1.15; font-weight: 700; letter-spacing: .04em;
  white-space: pre-wrap; user-select: none; -webkit-user-select: none;
}
.logo-line img { user-select: none; -webkit-user-select: none; -webkit-user-drag: none; }
.title-outline-text { display: inline-block; position: relative; color: inherit; }
.title-outline-ghost {
  position: absolute; left: 0; top: 0; z-index: 0; white-space: pre;
  color: transparent; -webkit-text-fill-color: transparent; pointer-events: none;
}
.title-outline-core {
  position: relative; z-index: 1; color: inherit;
  -webkit-text-fill-color: transparent;
}
.title-caret {
  display: inline-block; width: .08em; height: .86em; margin-left: .08em;
  vertical-align: -.08em; background: currentColor; box-shadow: 0 0 .18em currentColor;
  animation: title-caret .42s steps(1) infinite;
}
@keyframes title-caret { 50% { opacity: .18; } }
.tic { display: inline-block; position: relative; height: 1em; vertical-align: -0.22em; margin: 0 .08em; }
.tic img { height: 1em; width: auto; display: block; mix-blend-mode: screen; }
.tic img.ov { position: absolute; left: 0; top: 0; }
.tic-mask { height: 1em; vertical-align: -0.22em; margin: 0 .08em; }
.tic-mask img { height: 1em; width: auto; }
.tic-mask i.m { background: var(--tic-fill, var(--acc)); }
.tic-mask i.c, .tic-mask i.r { filter: blur(var(--tic-abblur, 0px)); }
.tier-badge {
  position: absolute; left: 50%; transform: translateX(-50%); bottom: -16px;
  font: 500 9px/1 "DM Mono", ui-monospace, monospace; letter-spacing: 1px;
  font-family: var(--ui-font, "DM Mono", ui-monospace, monospace);
  color: var(--dim); text-transform: uppercase; text-shadow: none; pointer-events: none;
}
/* settings page: item-art selector + preview strip */
.art-row { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.art-cell {
  border: 1px solid var(--border); background: var(--bg2); cursor: pointer;
  padding: 10px 12px 9px; text-align: center; min-width: 150px;
  flex: 1 1 160px; max-width: 220px; color: var(--text);
  text-transform: none; letter-spacing: 0;
}
.art-cell:hover { border-color: var(--border2); }
.art-cell.on { border-color: var(--acc); box-shadow: 0 0 14px -4px var(--acc-glow); }
.art-box { height: 96px; display: flex; align-items: center; justify-content: center; }
.art-label { color: var(--muted); font-size: 10.5px; letter-spacing: 1.2px; text-transform: uppercase; margin-top: 6px; }
.art-cell.on .art-label { color: var(--acc); }
.art-desc { color: var(--dim); font-size: 10px; line-height: 1.35; margin-top: 4px; }
.art-choice-head { margin-top: 18px; margin-bottom: -4px; }

/* settings page */
.set-row { display: flex; align-items: center; gap: 8px; margin: 10px 0; flex-wrap: wrap; }
.set-k { width: 110px; flex: none; color: var(--muted); font-size: 11px;
  letter-spacing: 1.5px; text-transform: uppercase; }
.set-val { color: var(--acc); font-size: 12px; min-width: 46px; text-align: right; }
/* A rule the UI states rather than a control it offers: sits where a toggle
   would, reads as settled fact, never as a dead button. */
.set-fixed { color: var(--acc-dim); font-size: 11px; letter-spacing: 1px;
  text-transform: uppercase; border: 1px solid var(--border2); padding: 2px 7px; }
input[type=range] {
  flex: 1; max-width: 320px; accent-color: var(--acc); background: transparent;
  height: 22px; cursor: pointer;
}

/* shared chips and per-browser palette */
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.chip {
  border: 1px solid var(--border2); background: var(--panel2); color: var(--text);
  padding: 2px 10px; font-size: 12px; cursor: pointer; text-transform: none; letter-spacing: 0;
}
.chip:hover, .chip.sel { border-color: var(--acc); color: var(--acc); }
/* An anchor wearing the chip: same target, but it is a destination, so it
   keeps the href behaviours a button cannot have. */
a.chip { display: inline-block; text-decoration: none; }
a.chip:hover { text-decoration: none; }
.palette-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(245px, 1fr));
  gap: 8px 16px; margin: 4px 0 12px;
}
.palette-control {
  display: flex; align-items: center; gap: 8px; min-width: 0;
}
.palette-control input[type=color] {
  width: 34px; height: 26px; padding: 1px; flex: none;
  border: 1px solid var(--border2); background: var(--panel2); cursor: pointer;
}
.palette-control .set-k { width: 100px; }
.palette-control .set-val { min-width: 66px; text-align: left; }

/* ---------- command bar ---------- */
.cmdwrap { position: relative; flex: none; z-index: 60; }
.cmdbar {
  display: flex; align-items: center; gap: 8px; height: 38px;
  background: var(--bg2); border-top: 1px solid var(--border2); padding: 0 14px;
}
.cmdbar .prompt { color: var(--acc); font-weight: 700; }
.cmdtoken {
  flex: none; border: 1px solid var(--acc-dim); background: var(--acc-faint);
  color: var(--acc); padding: 1px 9px; font-size: 12px; font-weight: 600;
  letter-spacing: 1px; text-transform: uppercase;
}
.cmdbar input {
  flex: 1; background: none; border: 0; outline: none !important; color: var(--bright);
  font: inherit; font-size: 13.5px; caret-color: var(--acc); padding: 0;
}
.cmdbar .hint { color: var(--dim); font-size: 11px; user-select: none; white-space: nowrap; }
.sugg {
  position: absolute; left: 0; right: 0; bottom: 100%;
  background: var(--panel); border: 1px solid var(--border2); border-bottom: 0;
  max-height: 320px; overflow-y: auto; box-shadow: 0 -12px 30px rgba(0,0,0,.5);
}
.sugg .sg {
  display: flex; align-items: center; gap: 9px; padding: 5px 14px; cursor: pointer;
  font-size: 12.5px; color: var(--text);
}
.sugg .sg.on { background: var(--acc-glow); color: var(--bright); box-shadow: inset 2px 0 0 var(--acc); }
.sugg .sg .tag {
  margin-left: auto; font-size: 10px; letter-spacing: 1px; color: var(--dim);
  text-transform: uppercase; flex: none;
}
.sugg .sg .d { color: var(--muted); font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; }
.sugg .sg img { flex: none; }
.sugg .sg b { color: var(--acc); font-weight: 600; }

/* inline suggestion list (lookup page) */
.inline-sugg { border: 1px solid var(--border2); background: var(--bg2); margin-top: 2px; }
.inline-sugg .sg { display: flex; align-items: center; gap: 9px; padding: 5px 11px; cursor: pointer; font-size: 12.5px; }
.inline-sugg .sg.on { background: var(--acc-glow); color: var(--bright); box-shadow: inset 2px 0 0 var(--acc); }
.inline-sugg .sg .d { color: var(--muted); font-size: 11.5px; }
.inline-sugg .sg .tag { margin-left: auto; font-size: 10px; letter-spacing: 1px; color: var(--dim); text-transform: uppercase; }

/* ---------- status bar ---------- */
.statusbar {
  display: flex; align-items: center; height: 25px; flex: none; gap: 16px;
  background: var(--panel2); border-top: 1px solid var(--border); padding: 0 14px;
  font-size: 11px; color: var(--muted); user-select: none; white-space: nowrap; overflow: hidden;
}
.statusbar .sp { flex: 1; }
.statusbar .seg b { color: var(--text); font-weight: 500; }
.statusbar .live-seg { overflow: hidden; text-overflow: ellipsis; }
.live-dot { color: var(--up); animation: blink 1.6s steps(1) infinite; }
.idle-dot { color: var(--dim); }
.statusbar .lat { color: var(--dim); }
.statusbar kbd { font-size: 10px; line-height: 15px; border-bottom-width: 1px; padding: 0 4px; }

/* command mode — while the command bar is focused, the rest of the
   terminal blurs so there's no doubt where keystrokes go */
.tape, .topbar, main { transition: filter .18s ease, opacity .18s ease; }
body.cmdfocus .tape, body.cmdfocus .topbar, body.cmdfocus main {
  filter: blur(2px) brightness(.68);
}
body.cmdfocus .cmdbar {
  border-top-color: var(--acc); background: var(--panel);
  box-shadow: 0 -8px 30px -12px var(--acc-glow);
}
body.cmdfocus .cmdbar .prompt { text-shadow: 0 0 8px var(--acc-dim); }

/* focus-lost state — the terminal dims like an inactive tmux pane */
.frame.unfocused .tape, .frame.unfocused .topbar, .frame.unfocused main,
.frame.unfocused .cmdwrap { opacity: .55; filter: grayscale(.35); }
.statusbar .focus-warn { color: var(--warn); animation: blink 1.4s steps(1) infinite; }
/* Takes the live segment's place while a refresh is failing: the figures on
   screen are real but held, and that is a different state from both "live" and
   "still loading". */
.statusbar .stale-seg { color: var(--warn); margin-right: auto; }

/* ---------- overlays ---------- */
.overlay {
  position: fixed; inset: 0; z-index: 80; background: rgba(4,6,8,.72);
  display: flex; align-items: center; justify-content: center;
}
.helpbox {
  background: var(--panel); border: 1px solid var(--acc-dim); width: min(760px, 92vw);
  max-height: 84vh; overflow-y: auto; padding: 18px 22px;
  box-shadow: 0 0 60px -10px var(--acc-glow);
}
.help-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.help-head h2 { margin: 0; }
.help-head button { padding: 3px 8px; }
.helpbox h2 { color: var(--acc); font-size: 13px; letter-spacing: 2px; margin: 14px 0 8px; text-transform: uppercase; }
.helpbox h2:first-child { margin-top: 0; }
.helpbox .krow { display: flex; gap: 10px; margin: 3px 0; font-size: 12.5px; align-items: baseline; }
.helpbox .krow .keys { width: 170px; flex: none; text-align: right; }
.helpbox .krow .what { color: var(--muted); }

.boot {
  position: fixed; inset: 0; z-index: 100; background: #05070a;
  padding: 6vh max(24px, 6vw); font-size: 13.5px; color: var(--acc);
  display: flex; flex-direction: column; align-items: center;
  transition: opacity .25s; cursor: pointer;
}
.boot.waiting { cursor: default; }
.boot.done { opacity: 0; pointer-events: none; }
.boot-title {
  flex: 1; min-height: 180px; width: 100%; overflow: hidden;
  display: flex; align-items: center; justify-content: center; position: relative;
}
.boot-title-stack {
  width: 100%; display: flex; flex-direction: column; align-items: center;
}
.boot-title .logo-line { max-width: 100%; }
.boot-credit {
  margin-top: 12px; color: #fff; font-weight: 700; line-height: 1;
  letter-spacing: .13em; white-space: nowrap; text-shadow: none;
  display: flex; align-items: baseline; justify-content: center;
  opacity: 0;
}
.boot-credit.show { opacity: 1; }
.boot-credit-by { flex: none; }
.boot-credit-name { flex: none; margin-left: .7em; letter-spacing: .13em; }
.boot-log { flex: none; width: min(720px, 100%); min-height: 190px; padding-bottom: 3vh; }
.boot.waiting .boot-log { min-height: 300px; }
.boot .bl { margin: 2px 0; white-space: pre; }
.boot .bl.dim { color: var(--dim); }
.boot .cur::after { content: "▊"; animation: blink .9s steps(1) infinite; }
.boot-queue {
  width: min(480px, 100%); margin: 18px 0 4px; padding: 13px 16px 15px;
  border: 1px solid var(--border); background: rgba(11,16,22,.8);
}
.boot-queue-label { color: var(--muted); font-size: 10px; letter-spacing: 1.6px; }
.boot-queue-pos { color: var(--acc); font-size: 38px; font-weight: 700; line-height: 1.15; margin-top: 3px; }
.boot-queue-pos small { color: var(--muted); font-size: 12px; font-weight: 400; }
.boot-queue-stat, .boot-admission-error { color: var(--muted); font-size: 11.5px; margin-top: 4px; }
.boot-admission-error { color: var(--down); }
.boot-queue-bar { height: 3px; margin-top: 11px; overflow: hidden; background: var(--border); }
.boot-queue-bar i {
  display: block; width: 30%; height: 100%; background: var(--acc);
  animation: boot-queue-scan 2.2s ease-in-out infinite;
}
@keyframes boot-queue-scan { 0% { margin-left: -30%; } 100% { margin-left: 100%; } }
.boot-login-row { display: flex; gap: 8px; margin-top: 10px; }
.boot-login-row input { flex: 1; letter-spacing: 3px; }
.boot-login { margin-left: auto; margin-right: auto; }
.boot-login .boot-admission-error { margin-top: 8px; }

@media (max-width: 900px) {
  .tape, .cmdbar .hint { display: none; }
  .topbar { padding: 0 10px; gap: 2px; }
  .brand { margin-right: 10px; }
  .tabs { flex: 1; }
  .topbar .sp { display: none; }
  .tab { padding: 0 8px; }
  .boot { padding: 4vh 24px; }
  .boot-log { min-height: 175px; padding-bottom: 1vh; }
}
@media (max-width: 900px) {
  /* Twelve tabs are 760 px of strip. Squeezed beside the brand and the league
     chip that left two and a half of them in view on a phone, behind a
     sideways scroll marked by a 2 px line — a navigation most of which you
     had to already know was there. Given its own full-width row it is all
     visible, and the hidden state is gone.

     A grid, not a wrap: ragged wrapping packed the twelve into three uneven
     rows and left a half-empty last one. Six even columns is two full rows;
     auto-fit was tried first and gave ten-then-two at tablet widths, which is
     the same raggedness by another route. Every target is the same size. */
  .topbar { height: auto; min-height: 42px; flex-wrap: wrap; padding-bottom: 0; }
  .topbar .brand { order: 1; height: 42px; }
  .topbar .league-chip { order: 2; margin-left: auto; }
  .tabs {
    order: 3; flex: 1 0 100%; overflow-x: visible; background: none;
    display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
    border-top: 1px solid var(--border);
  }
  .tab {
    height: 30px; font-size: 10px; letter-spacing: .3px; padding: 0 2px;
    justify-content: center; min-width: 0;
  }
}
/* Wide enough for all twelve on one line, still too narrow to sit beside the
   brand: the tab row stays full-width and becomes a single row. */
@media (min-width: 780px) and (max-width: 900px) {
  .tabs { grid-template-columns: repeat(12, minmax(0, 1fr)); }
}
@media (max-width: 700px) {
  /* The share bar is a peripheral cue for comparing rows at a glance. On a
     phone it is the widest thing in the table and the only reason the card
     list scrolls sideways at all, so it goes and the figures fit. */
  table.list td.share, table.list th.share { display: none; }
  /* A panel title is one nowrap line: title, rule, note. On a phone the note
     ran straight off the right edge — "chaos · click a", "exact row cou" —
     with the rule pushing it there. Dropping the rule and letting the note
     take its own line keeps every word of it. */
  .ptitle { flex-wrap: wrap; white-space: normal; row-gap: 2px; }
  .ptitle::after { display: none; }
  .ptitle .pnote { flex: 1 0 100%; }
  /* A wrapping cell is prose — a modifier line, a cohort label — and
     `table.list td { width: 1px }` gave it the least width in the table, so
     the numbers kept their columns and the sentence beside them wrapped one
     word at a time. The unique page's modifier tables ran fifty phone-screens
     tall on that. Prose takes the majority here and the figures fit around it. */
  table.list td.wrap, table.list th.wrap { width: auto; min-width: 55%; }
  /* Columns marked minor are the ones that read zero on almost every row.
     They are worth a glance on a desk and they are worth nothing on a phone,
     where every column they push off the right edge is a column of figures
     the reader came for. */
  th.minor, td.minor { display: none; }
  /* Cell padding is 18px a column; over a dozen columns that is most of a
     phone screen spent on gaps. */
  td, th { padding-left: 6px; padding-right: 6px; }
  table.jobs th, table.jobs td { padding-left: 7px; padding-right: 7px; }
  /* The digit on each tab is its keyboard shortcut. There is no keyboard
     here, and dropping it fits three more tabs in the strip. */
  .tab i { display: none; }

  /* Pin the row's identity while the figures scroll under it. Even after the
     minor columns go, a list is wider than a phone — and scrolled sideways
     without this, every row became an anonymous line of numbers with the name
     that owns them off the left edge. The highlight colours are translucent
     by design, so they are composited OVER an opaque panel here rather than
     replacing it; a plain translucent background would let the scrolled
     figures show through the pinned name. */
  table.list td:first-child, table.list th:first-child {
    position: sticky; left: 0; z-index: 1;
    background: var(--panel);
    box-shadow: 1px 0 0 var(--border);
  }
  table.list tr.row:hover td:first-child {
    background: linear-gradient(var(--acc-faint), var(--acc-faint)), var(--panel);
  }
  table.list tr.sel td:first-child {
    background: linear-gradient(var(--acc-glow), var(--acc-glow)), var(--panel) !important;
    box-shadow: inset 2px 0 0 var(--acc), 1px 0 0 var(--border);
  }
}
/* Instructions for keys the device does not have. Keyed on the pointer, not
   the viewport: a narrow window on a desktop still has a keyboard, and a wide
   tablet still has none. */
@media (hover: none) and (pointer: coarse) {
  .kbd-only { display: none; }
  /* iOS Safari zooms the whole page in when a field under 16px takes focus,
     and does not zoom back out when it loses focus — so one tap on the command
     bar left the terminal permanently magnified and scrolled sideways. 16px is
     the threshold, not a preference. */
  input[type=text], input[type=number], input[type=password],
  textarea, select, .cmdbar input { font-size: 16px; }
  /* Rows, chips and sort headers are pointer-sized on a desk and finger-sized
     here. The row is the primary target on five index pages. */
  table.list td, table.rows td { padding-top: 8px; padding-bottom: 8px; }
  .chip, .pager button { padding: 8px 12px; }
  th.sortable { padding-top: 8px; padding-bottom: 8px; }
  /* The explainer marker is a 12px square and the only explainer affordance a
     phone has. Padded out to a finger and pulled back by the same amount, so
     the target grows and nothing moves. */
  .hint { padding: 9px; margin: -9px; }
}
@media (max-width: 600px) {
  main { padding: 14px 10px 22px; }
  .page-title { align-items: flex-start; gap: 4px 10px; }
  .page-title .sub { order: 3; flex: 1 1 100%; }
  .league-chip { padding: 3px 7px; }
  .league-chip i { display: none; }
  .statusbar { gap: 10px; padding: 0 10px; }
  .statusbar .shortcut-seg, .statusbar .lat, .statusbar .sp { display: none; }
  /* The page name is the highlighted tab two rows up and the league is in the
     header chip AND every page title; the ingest figures are nowhere else, and
     they were the ones being ellipsised mid-digit. */
  .statusbar .page-seg, .statusbar .league-seg { display: none; }
  .statusbar .live-seg { margin-right: auto; max-width: none; }
  .helpbox { padding: 14px 16px; }
  .helpbox .krow { display: grid; gap: 1px; margin: 7px 0; }
  .helpbox .krow .keys { width: auto; text-align: left; }
}
body.cmdfocus .cmdbar { outline: 1px solid var(--acc); outline-offset: -1px; }
@media (prefers-reduced-motion: reduce) {
  .tape-inner, body.crt-flicker.crt::before, body.crt-roll .rollbar,
  .loading::after, .live-dot, .statusbar .focus-warn, .title-caret, .boot .cur::after,
  tr.flash td {
    animation: none;
  }
  /* The blur that says "keystrokes go here" is motion the moment it appears;
     the outline above carries the same meaning and holds still. */
  body.cmdfocus .tape, body.cmdfocus .topbar, body.cmdfocus main { filter: none; }
  .tape, .topbar, main, .boot { transition: none; }
  .boot-queue-bar i { animation: none; width: 100%; opacity: .45; }
}

/* jobs: derivation control plane */
table.jobs { width: 100%; border-collapse: collapse; }
table.jobs th, table.jobs td { padding: 7px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
table.jobs th { color: var(--muted); font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase; text-align: left; font-weight: 400; }
table.jobs th.num, table.jobs td.num { text-align: right; }
table.jobs tr.off td { opacity: .5; }
table.jobs tr.behind td .bar i { background: var(--warn); }
table.jobs td.frow { display: flex; gap: 5px; }
.bar { height: 5px; background: var(--bg2); border: 1px solid var(--border); min-width: 90px; }
.bar i { display: block; height: 100%; background: var(--acc); }
.warn-t { color: var(--warn); font-size: 10px; }
.err-t { color: var(--down); font-size: 10px; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.err-t.open { max-width: 460px; overflow: visible; text-overflow: clip; white-space: normal; line-height: 1.45; }
.bar.wide { min-width: 340px; height: 9px; }
.bar i.arc { background: var(--blue); }

/* Share column: the slack absorber on a list table.
   A three-column list stretched over 1560px put a card's name at the far left
   and its counts at the far right, so reading one row was a sweep across the
   whole screen. One column takes all the leftover width, which pulls the real
   columns into a readable cluster on the left — and then spends the space it
   took on the magnitude those numbers describe, so the void becomes the
   at-a-glance ranking the sorted list is already implying.

   `width: 100%` alone is only a hint to the auto table algorithm, which kept
   handing the slack back to the name column; pinning every other cell to 1px
   collapses them to min-content (they are all nowrap) and leaves exactly one
   column that can grow.

   The bar is kept deliberately faint: at full accent it was a wall of green
   that outweighed every number on the page, which is the opposite of the job.
   It is a peripheral cue — read the figures, see the shape. */
table.list td, table.list th { width: 1px; }
table.list td.share, table.list th.share { width: 100%; padding-left: 16px; }
.share .bar { min-width: 0; height: 3px; background: none; border: none; }
.share .bar i { background: var(--acc); opacity: .22; }
tr.sel .share .bar i, tr.row:hover .share .bar i { opacity: .6; }
/* A bar and the figure it states, on one line. Stacked, the percentage fell
   into the gap under its own bar and read as a label on the row below it. */
.barcell { display: flex; align-items: center; gap: 7px; }
.barcell .bar { flex: none; width: 84px; min-width: 0; }
.barcell .dim { flex: none; font-size: 10.5px; font-variant-numeric: tabular-nums; }
/* The control pair is one control, and it is the column the wider progress
   cells steal from first: allowed to wrap it stacked its two buttons and made
   every row in the table half again as tall. */
table.jobs td.frow { flex-wrap: nowrap; margin-bottom: 0; }
.arcbar { align-items: center; gap: 10px; padding: 4px 2px; }
/* jobs: which job holds the pass right now */
.run-now { color: var(--acc); font-weight: 600; }
table.jobs tr.live td { background: var(--acc-faint); }
table.jobs tr.live td .bar i { background: var(--acc); }
/* item lifecycle: a verdict we made, and one we withdrew */
.verdict { margin: 3px 0 0 14px; font-size: 11px; }
.verdict s { color: var(--muted); }
.retr-why { color: var(--down); margin-left: 8px; }
.b.retr { background: var(--down); color: #000; }
.b.excl { background: var(--warn); color: #000; }
.excl-why { color: var(--warn); margin-left: 8px; }
.timeline .evt { color: var(--muted); margin: 0 8px 0 6px; font-variant-numeric: tabular-nums; }

/* rare comparables: the item being built, one clause a row */
.specrow {
  display: flex; align-items: center; gap: 10px; padding: 5px 0;
  border-bottom: 1px solid var(--row-border); font-size: 12.5px;
}
.specrow .specname { flex: 1; min-width: 0; }
.specband { display: flex; align-items: center; gap: 6px; flex: none; }
/* Wide enough for a four-digit roll and no wider: these sit two to a row and
   the observed range beside them is what makes the pair readable. */
.specband input { width: 74px; padding: 3px 6px; text-align: right; }
@media (max-width: 700px) {
  .specrow { flex-wrap: wrap; }
  .specband { width: 100%; }
}
