/* Clutch — monochrome instrument panel.
   ===================================================================
   Chrome is greyscale. Semantic status colour (--ok, --warn, --danger,
   --info) is permitted on status MARKS, band rules, tape fills and
   score arcs only — never on chrome, backgrounds or blocks of text.
   A test enforces that those eight hexes appear nowhere but in their
   own token declarations inside the three theme blocks.

   Air lives BETWEEN blocks (--s5) and around the page, never INSIDE a
   data object: cells keep --s1 padding and rows keep --row-h. A dense
   table beside a generous margin reads as calm; the reverse reads as
   a spreadsheet with a headache.

   Geometry is server-rendered SVG presentation attributes (width, x,
   points, d, stroke-dasharray) — the CSP forbids style="". The only
   [data-pct] step table in this file is the pair of live-refreshed GPU
   meters on /forge, because JS may set an attribute but not a style.

   Uppercase survives in three roles only: .gauge span, .rail-label,
   and .stat .k / .kv dt / .facts dt. Everything else is sentence case.
   border-radius is 0 everywhere.
   =================================================================== */

:root {
  --bg: #ffffff;
  --bg-2: #f6f6f6;
  --bg-3: #ececec;
  --ink: #0a0a0a;
  --ink-2: #3d3d3d;
  --ink-3: #6e6e6e;
  --line: #d5d5d5;
  --line-2: #e9e9e9;
  --line-3: #bdbdbd;
  --shadow: rgba(0, 0, 0, .10);

  /* Status. The only non-grey values in the panel. */
  --ok: #1f6b3f;
  --warn: #8a5a00;
  --danger: #b3261e;
  --info: #1f5c8a;

  /* Ink ramp: derived, never declared twice. --ink flips with the theme,
     so amber darkens on white and lightens on near-black by itself. The
     first declaration is the fallback where color-mix is unsupported. */
  --ok-ink: var(--ok);
  --ok-ink: color-mix(in srgb, var(--ok) 72%, var(--ink));
  --warn-ink: var(--warn);
  --warn-ink: color-mix(in srgb, var(--warn) 72%, var(--ink));
  --danger-ink: var(--danger);
  --danger-ink: color-mix(in srgb, var(--danger) 72%, var(--ink));
  --info-ink: var(--info);
  --info-ink: color-mix(in srgb, var(--info) 72%, var(--ink));

  /* Washes. Always painted over an opaque --bg-2, declared first, so a
     browser without color-mix still gets a legible band. */
  --ok-wash: color-mix(in srgb, var(--ok) 8%, var(--bg-2));
  --warn-wash: color-mix(in srgb, var(--warn) 10%, var(--bg-2));
  --danger-wash: color-mix(in srgb, var(--danger) 8%, var(--bg-2));
  --info-wash: color-mix(in srgb, var(--info) 8%, var(--bg-2));

  --s0: 2px;
  --s-half: 4px;
  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 32px;
  --s5: 48px;
  --s6: 64px;

  --rail: 13rem;
  --bar: 56px;
  --head: 44px;
  --gutter: 18px;
  --mark: 8px;
  --spine: 3px;
  --tape-h: 6px;
  --tape-w: 120px;
  --ring: 34px;
  --row-h: 28px;
  --aside: 20rem;

  --t-micro: 10px;
  --t-xs: 11px;
  --t-sm: 12px;
  --t-base: 13px;
  --t-md: 15px;
  --t-lg: 18px;
  --t-xl: 22px;
  --t-2xl: 28px;
  --t-hero: 32px;
  --track: .14em;

  --sans: "Segoe UI Variable Text", "Segoe UI", Inter, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --sans-display: "Segoe UI Variable Display", "Segoe UI Variable Text", "Segoe UI", Inter, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --dur-fast: 90ms;
  --dur: 140ms;
  --ease: cubic-bezier(.2, .6, .2, 1);
  --shadow-lift: 0 2px 12px var(--shadow);

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0a0a0a;
    --bg-2: #141414;
    --bg-3: #1f1f1f;
    --ink: #f2f2f2;
    --ink-2: #bcbcbc;
    --ink-3: #8d8d8d;
    --line: #303030;
    --line-2: #202020;
    --line-3: #4a4a4a;
    --shadow: rgba(0, 0, 0, .6);
    --ok: #5fbf83;
    --warn: #e0a437;
    --danger: #ff6f63;
    --info: #6ab3e8;
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --bg: #0a0a0a;
  --bg-2: #141414;
  --bg-3: #1f1f1f;
  --ink: #f2f2f2;
  --ink-2: #bcbcbc;
  --ink-3: #8d8d8d;
  --line: #303030;
  --line-2: #202020;
  --line-3: #4a4a4a;
  --shadow: rgba(0, 0, 0, .6);
  --ok: #5fbf83;
  --warn: #e0a437;
  --danger: #ff6f63;
  --info: #6ab3e8;
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-base);
  line-height: 1.45;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
main a:not(.btn):not(.tab):not(.chip):not(.badge):not(.filter):not(.instr):not(.plain) {
  text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--line);
}
main a:not(.btn):not(.tab):not(.chip):not(.badge):not(.filter):not(.instr):not(.plain):hover {
  text-decoration-color: currentColor;
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 60;
  background: var(--ink); color: var(--bg); padding: var(--s1) var(--s2);
}
.skip:focus { left: var(--s1); top: var(--s1); }

/* ── Shell ──────────────────────────────────────────────────────────── */

.shell {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  grid-template-rows: var(--bar) minmax(0, 1fr);
  min-height: 100vh;
}

.shell.bare { grid-template-columns: minmax(0, 1fr); }

.topbar {
  grid-column: 1 / -1;
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: stretch; gap: 0;
  height: var(--bar);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}

.brand {
  display: flex; align-items: center;
  width: var(--rail); flex: 0 0 var(--rail);
  padding: 0 var(--s2);
  border-right: 1px solid var(--line);
  font-family: var(--sans-display);
  font-size: var(--t-md); font-weight: 600; letter-spacing: -.01em;
}
.brand b { font-weight: 600; }
.brand span {
  margin-left: var(--s1);
  font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-3);
}

/* The readout hides its scrollbar — a 6px bar inside a 56px bar is noise —
   and owes the reader a visible edge fade instead, at every width (§4.5). */
.readout {
  display: flex; align-items: stretch; overflow-x: auto; scrollbar-width: none; flex: 1 1 auto;
  -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 20px), transparent 100%);
  mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 20px), transparent 100%);
}
.readout::-webkit-scrollbar { display: none; }

.gauge {
  display: flex; flex-direction: column; justify-content: center;
  /* Never shrink: the readout scrolls instead. A shrunk gauge cannot hold its
     own nowrap value, and the overflow paints over the next gauge. */
  flex: 0 0 auto;
  padding: 0 var(--s2); min-width: 5.5rem;
  border-right: 1px solid var(--line-2);
  white-space: nowrap;
}
.gauge b {
  display: flex; align-items: center; gap: var(--s-half);
  font-size: var(--t-lg); font-weight: 700; letter-spacing: -.02em; line-height: 1.1;
  font-variant-numeric: tabular-nums slashed-zero;
}
.gauge span {
  font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-3);
}
.gauge.host { min-width: 0; }
.gauge.host b { font-family: var(--mono); font-size: var(--t-base); font-weight: 500; letter-spacing: 0; }
/* The clock (§4.1): stated once, in the chrome, and never repeated on a page. */
.gauge.g-clock b { white-space: nowrap; }
/* Non-nominal: the mark carries the colour and the label gains weight.
   The number itself stays ink — chrome never turns red. */
.gauge.alert span { color: var(--ink); font-weight: 600; }
.gauge b .muted { font-weight: 400; }

.topbar-end { display: flex; align-items: center; gap: var(--s1); padding: 0 var(--s2); margin-left: auto; }

.theme-toggle, .palette-open, .alerts-link {
  display: inline-flex; align-items: center; gap: var(--s1);
  height: 28px; padding: 0 var(--s1);
  background: none; border: 1px solid var(--line); color: var(--ink-2);
  font: inherit; font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase;
  cursor: pointer;
}
.theme-toggle:hover, .palette-open:hover, .alerts-link:hover { border-color: var(--ink); color: var(--ink); background: none; }
.theme-toggle i {
  display: block; width: 11px; height: 11px; border-radius: 50%;
  border: 1px solid currentColor;
  background: linear-gradient(90deg, currentColor 0 50%, transparent 50% 100%);
}
.palette-open kbd { border-color: var(--line-3); background: none; color: var(--ink-3); letter-spacing: 0; }

/* The unread badge rides the Alerts link and the top bar alike. */
.unread {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 4px;
  border: 1px solid var(--ink); background: var(--ink); color: var(--bg);
  font-size: var(--t-micro); font-weight: 700; font-style: normal; line-height: 1;
}
.unread.none { background: none; color: var(--ink-3); border-color: var(--line-3); }

/* ── Rail ───────────────────────────────────────────────────────────── */

.rail {
  position: sticky; top: var(--bar); align-self: start;
  height: calc(100vh - var(--bar)); overflow-y: auto;
  background: var(--bg-2);
  border-right: 1px solid var(--line);
  padding: var(--s2) 0 var(--s3);
}

.rail a {
  display: flex; align-items: center; gap: var(--s1);
  padding: var(--s1) var(--s2);
  font-size: var(--t-base); font-weight: 500; color: var(--ink-2);
  border-left: 2px solid transparent;
}
.rail a:hover { background: var(--bg-3); color: var(--ink); }
.rail a[aria-current="page"] {
  background: var(--ink); color: var(--bg); border-left-color: var(--ink); font-weight: 600;
}
.rail a em {
  margin-left: auto; font-style: normal; font-size: var(--t-xs); color: var(--ink-3);
  font-variant-numeric: tabular-nums slashed-zero;
}
.rail a[aria-current="page"] em { color: var(--bg); opacity: .7; }
.rail a .pip { margin-left: auto; }
.rail a .pip + em { margin-left: var(--s-half); }

.pip {
  display: inline-block; width: 4px; height: 4px; flex: 0 0 auto;
  background: currentColor; color: var(--ink-3);
}
.pip.down, .pip.fail { color: var(--danger-ink); }
.pip.warn { color: var(--warn-ink); }
.pip.info { color: var(--info-ink); }
.rail a[aria-current="page"] .pip { color: var(--bg); }

.rail-label {
  margin: 0;
  padding: var(--s3) var(--s2) var(--s-half);
  font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-3);
}
.rail-group:first-child .rail-label { padding-top: 0; }

/* ── Main ───────────────────────────────────────────────────────────── */

main { min-width: 0; padding: var(--s3) var(--s3) var(--s6); }

.pagehead {
  position: sticky; top: var(--bar); z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s2);
  min-height: var(--head);
  padding: 0 0 var(--s1);
  margin: 0 0 var(--s3);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.pagehead h1 {
  margin: 0;
  font-family: var(--sans-display);
  font-size: var(--t-xl); font-weight: 600; letter-spacing: -.015em;
}
.pagehead .sub { margin: 2px 0 0; font-size: var(--t-sm); color: var(--ink-3); }
.pagehead-actions { display: flex; gap: var(--s1); flex-wrap: wrap; }

h1, h2, h3 { margin: 0 0 var(--s1); font-weight: 600; }
h1 { font-family: var(--sans-display); font-size: var(--t-xl); letter-spacing: -.015em; }
h2 {
  display: flex; align-items: baseline; gap: var(--s2);
  margin: var(--s5) 0 var(--s2);
  font-size: var(--t-base); font-weight: 600; letter-spacing: 0; text-transform: none;
  color: var(--ink);
}
h2::after { content: ""; flex: 1 1 auto; height: 1px; background: var(--line); }
h2:first-child { margin-top: 0; }
h3 { font-size: var(--t-base); letter-spacing: -.005em; }
h4 { margin: 0 0 var(--s-half); font-size: var(--t-sm); font-weight: 600; }
p { margin: 0 0 var(--s2); }
p:last-child { margin-bottom: 0; }

.muted { color: var(--ink-3); }
.mono, pre, code, kbd, output { font-family: var(--mono); font-variant-ligatures: none; }
.num, td.n, .stat .v, .kv dd, .facts dd, .mono, pre, code, output {
  font-variant-numeric: tabular-nums slashed-zero;
}
kbd {
  border: 1px solid var(--line); padding: 0 var(--s-half); font-size: var(--t-xs);
  background: var(--bg-2);
}

/* One plain sentence, composed from the page's own numbers. Glance pages
   only, once per page, always in --sans. */
.lede {
  max-width: 68ch;
  margin: 0 0 var(--s3);
  font-size: var(--t-md); line-height: 1.5; color: var(--ink-2);
}
.lede b { color: var(--ink); font-weight: 600; }

.stamp {
  margin: 0;
  font-size: var(--t-xs);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums slashed-zero;
}
h2 .tally {
  font-size: var(--t-xs);
  font-weight: 400;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums slashed-zero;
}

/* ── Filter bar ─────────────────────────────────────────────────────── */

.filterbar {
  position: sticky; top: calc(var(--bar) + var(--head) + var(--s1)); z-index: 18;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-half) var(--s2);
  min-height: 40px;
  padding: var(--s-half) 0;
  margin: 0 0 var(--s3);
  background: var(--bg);
  border-bottom: 1px solid var(--line-2);
}
.filterbar .facet { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-half); }
.filterbar .facet-label {
  margin-right: var(--s-half);
  font-size: var(--t-micro); letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
}
.filter {
  display: inline-flex; align-items: baseline; gap: var(--s-half);
  padding: 2px var(--s1);
  border: 1px solid var(--line);
  font-size: var(--t-sm); color: var(--ink-2);
}
.filter:hover { border-color: var(--ink); color: var(--ink); }
.filter em { font-style: normal; font-size: var(--t-xs); color: var(--ink-3); font-variant-numeric: tabular-nums slashed-zero; }
.filter[aria-current="true"] { background: var(--ink); border-color: var(--ink); color: var(--bg); font-weight: 600; }
.filter[aria-current="true"] em { color: var(--bg); opacity: .7; }
.filterbar form { display: flex; gap: var(--s-half); align-items: center; margin-left: auto; }
.filterbar input[type="search"], .filterbar input[type="text"] { width: 14rem; padding: 3px var(--s1); }

/* ── Sheet, banks, split ────────────────────────────────────────────── */

.sheet { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--s3); align-items: start; }
.sheet > * { grid-column: span 12; min-width: 0; }
.sheet > .span-8 { grid-column: span 8; }
.sheet > .span-6 { grid-column: span 6; }
.sheet > .span-4 { grid-column: span 4; }

.block { margin: 0 0 var(--s5); }
.block:last-child { margin-bottom: 0; }

/* A bank of instruments. Each cell paints its own hairline as a 1px
   box-shadow; adjacent shadows meet inside the 1px gap, and overflow
   clips the outer ones so the bank's border is the only frame. Doing it
   this way — rather than colouring the grid's background and letting the
   gaps show it — means a five-up bank that wraps to three columns leaves
   a cell the page colour, not a slab of grey. In dark the internal
   hairlines drop to --line-2 or the mesh lights up. */
.bank {
  display: grid; gap: 1px; overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--line);
  margin: 0 0 var(--s3);
}
.b5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.b4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.b3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.b2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

.bank > * { box-shadow: 0 0 0 1px var(--line); }
.bank > .instr { display: flex; flex-direction: column; }
.bank > .instr .kv { margin-bottom: var(--s2); }
.bank > .instr .foot { margin-top: auto; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .bank > * { box-shadow: 0 0 0 1px var(--line-2); }
}
:root[data-theme="dark"] .bank > * { box-shadow: 0 0 0 1px var(--line-2); }

.instr { display: block; background: var(--bg); padding: var(--s2); min-width: 0; color: inherit; }
a.instr:hover { background: var(--bg-2); }
.instr-head { display: flex; align-items: baseline; gap: var(--s1); margin-bottom: var(--s-half); }
.instr-head .name { font-size: var(--t-base); font-weight: 600; }
.instr-head .kind {
  margin-left: auto;
  font-size: var(--t-micro); letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
}
.instr .summary { display: block; margin: 0 0 var(--s2); font-size: var(--t-sm); color: var(--ink-2); }
.instr .foot { display: block; font-size: var(--t-xs); color: var(--ink-3); }
.instr .foot.links { display: flex; flex-wrap: wrap; gap: var(--s2); }

.section { display: flex; flex-direction: column; gap: var(--s2); }
.section .instr-head { margin-bottom: 0; }
.section .kv { margin: 0; }
.section .foot {
  padding-top: var(--s1);
  border-top: 1px solid var(--line-2);
}

.split { display: grid; grid-template-columns: minmax(0, 1fr) var(--aside); gap: var(--s5); align-items: start; }
.reading { max-width: 72ch; }
.aside { min-width: 0; }
.aside > * + * { margin-top: var(--s3); }

/* ── Marks and dots ─────────────────────────────────────────────────── */

/* Shape-encoded, so colour is never the only channel. Always in the
   gutter, always beside its own word (visible or .sr). */
.mark {
  position: relative; display: inline-block; flex: 0 0 auto;
  width: var(--mark); height: var(--mark);
  border: 1px solid currentColor;
  color: var(--ink-3);
  vertical-align: -1px;
}
.mark.ok, .mark.up, .mark.pass, .mark.done, .mark.active { color: var(--ok-ink); background: currentColor; }
.mark.warn, .mark.degraded { color: var(--warn-ink); }
.mark.warn::after, .mark.degraded::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 50%; background: currentColor;
}
.mark.down, .mark.fail, .mark.failed, .mark.error { color: var(--danger-ink); background: currentColor; }
.mark.down::after, .mark.fail::after, .mark.failed::after, .mark.error::after {
  content: ""; position: absolute; left: -1px; right: -1px; top: 2px; height: 2px; background: var(--bg);
}
.mark.info { color: var(--info-ink); background: currentColor; }
.mark.unknown, .mark.none { color: var(--ink-3); }
.mark.paused, .mark.idle, .mark.nodata, .mark.cancelled {
  color: var(--ink-3);
  background: repeating-linear-gradient(135deg, var(--ink-3) 0 2px, transparent 2px 4px);
}
/* Spec §4.1 resolves the old collision: healthy-green RUNNING is an alias of
   ok, and the blue in-flight mark a command wears is WORKING. */
.mark.running { color: var(--ok-ink); background: currentColor; }
.mark.working { color: var(--info-ink); }
.mark.working::after {
  content: ""; position: absolute; left: 1px; right: 1px; top: 1px; bottom: 1px; background: currentColor;
}
.mark.queued { color: var(--ink-3); border-style: dashed; }

/* The three other marks M2 adds. Each is shape-encoded, so the four read
   apart in greyscale as well as they do in colour.
   stopped  — ink outline with one bar: switched off on purpose, not broken.
   missing  — amber outline slashed through: recorded here, not on the machine.
   neutral  — a soft filled grey block: seen, deliberately not watched. */
.mark.stopped { color: var(--ink-2); }
.mark.stopped::after {
  content: ""; position: absolute; left: 1px; right: 1px; top: 50%; height: 2px;
  margin-top: -1px; background: currentColor;
}
.mark.missing {
  color: var(--warn-ink);
  background: linear-gradient(45deg, transparent 43%, currentColor 43% 57%, transparent 57%);
}
.mark.neutral { color: var(--line-3); background: currentColor; border-color: var(--ink-3); }

.marked { display: inline-flex; align-items: center; gap: var(--s1); }
td.gut, th.gut { width: var(--gutter); padding-right: 0; }

/* Kept verbatim: /ssh writes dot.className = "dot " + state at runtime. */
.dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; border: 1px solid var(--ink); flex: 0 0 auto; }
.dot.up { background: var(--ink); }
.dot.down { background: var(--danger); border-color: var(--danger); }
.dot.unknown { background: none; border-color: var(--ink-3); }
.dot.warn { background: var(--warn); border-color: var(--warn); }
.dot.paused {
  border-color: var(--ink-3);
  background: repeating-linear-gradient(135deg, var(--ink-3) 0 2px, transparent 2px 4px);
}

/* ── Tape, trace, bars, ring ────────────────────────────────────────── */

.tape { display: block; width: var(--tape-w); height: var(--tape-h); }
.tape .track { fill: var(--line-3); }
.tape .fill { fill: var(--ink-2); }
.tape.ok .fill { fill: var(--ok-ink); }
.tape.warn .fill { fill: var(--warn-ink); }
.tape.down .fill, .tape.fail .fill { fill: var(--danger-ink); }
.tape.info .fill { fill: var(--info-ink); }
.tape .bug { fill: var(--ink); }
.tapes { display: grid; gap: var(--s1); }
.tape-row {
  display: grid; grid-template-columns: 7rem var(--tape-w) minmax(0, 1fr);
  gap: var(--s1); align-items: center; font-size: var(--t-sm);
}
.tape-row .k { font-size: var(--t-xs); color: var(--ink-3); overflow-wrap: anywhere; }
.tape-row .v { font-variant-numeric: tabular-nums slashed-zero; }

.trace { display: block; width: 100%; height: 28px; }
.trace polyline { fill: none; stroke: var(--ink-2); stroke-width: 1; }
.trace circle { fill: var(--ink-2); stroke: none; }
.trace .gap { fill: url(#clutch-hatch); }
.trace .axis { stroke: var(--line); stroke-width: 1; }
.trace.ok polyline { stroke: var(--ok-ink); }
.trace.warn polyline { stroke: var(--warn-ink); }
.trace.down polyline { stroke: var(--danger-ink); }
.hatch-line { stroke: var(--ink-3); }
svg.defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.bars { display: block; width: 100%; height: 34px; }
.bars rect { fill: var(--ink-2); }
.bars rect.ok { fill: var(--ok-ink); }
.bars rect.warn { fill: var(--warn-ink); }
.bars rect.down { fill: var(--danger-ink); }
.bars rect.nodata { fill: url(#clutch-hatch); }
.bars rect.track { fill: var(--line-2); }

.ring { display: block; width: var(--ring); height: var(--ring); }
.ring .track { stroke: var(--line-3); }
.ring .arc { stroke: var(--ink); }
.ring.ok .arc { stroke: var(--ok-ink); }
.ring.warn .arc { stroke: var(--warn-ink); }
.ring.down .arc { stroke: var(--danger-ink); }
.ring text { fill: var(--ink); font-family: var(--sans); font-size: 12px; font-weight: 700; }
.ringed { display: inline-flex; align-items: center; gap: var(--s1); }
.ringed .label { font-size: var(--t-sm); }

/* Eight parallel bars beat eight rings. */
.smallmult { display: grid; gap: var(--s1); max-width: 44rem; margin: 0 0 var(--s3); }
.sm-row {
  display: grid; grid-template-columns: 9rem minmax(0, 1fr) 2.5rem;
  gap: var(--s1); align-items: center; font-size: var(--t-sm);
}
.sm-row .k { color: var(--ink-2); }
.sm-row .tape { width: 100%; }
.sm-row .n { text-align: right; font-variant-numeric: tabular-nums slashed-zero; }

.stack { display: block; width: 100%; height: 10px; border: 1px solid var(--line); margin: 0 0 var(--s1); }
.stack rect { fill: var(--ink-3); stroke: var(--bg); stroke-width: 1; }
.stack rect:nth-child(2n) { fill: var(--ink-2); }
.stack rect:nth-child(3n) { fill: var(--line-3); }
.stack-key { display: flex; flex-wrap: wrap; gap: var(--s-half) var(--s2); font-size: var(--t-xs); color: var(--ink-3); }

/* Router → Tunnel → Forge. */
.chain { display: flex; flex-wrap: wrap; align-items: stretch; border: 1px solid var(--line); margin: 0 0 var(--s3); }
.chain .link { flex: 1 1 8rem; padding: var(--s2); border-right: 1px solid var(--line); min-width: 0; }
.chain .link:last-child { border-right: 0; }
.chain .link .k { display: flex; align-items: center; gap: var(--s1); font-size: var(--t-sm); font-weight: 600; }
.chain .link .v { margin-top: 2px; font-size: var(--t-xs); color: var(--ink-3); overflow-wrap: anywhere; }

/* Mode switch: three segments, one inked. */
.segment { display: inline-flex; border: 1px solid var(--ink); margin: 0 0 var(--s2); }
.segment button, .segment .seg {
  border: 0; border-right: 1px solid var(--line); background: var(--bg); color: var(--ink-2);
  padding: var(--s1) var(--s3); font: inherit; font-size: var(--t-base); font-weight: 600; cursor: pointer;
}
.segment > *:last-child { border-right: 0; }
.segment button:hover { background: var(--bg-3); color: var(--ink); }
.segment [aria-pressed="true"], .segment .on { background: var(--ink); color: var(--bg); }
.segment [aria-pressed="true"]:hover { background: var(--ink); color: var(--bg); }
.segment button:disabled { color: var(--ink-3); cursor: progress; }
.segment.switching [aria-pressed="true"] { animation: seg-pulse 1.2s var(--ease) infinite; }
.segment.switching .seg.on { animation: seg-pulse 1.2s var(--ease) infinite; }
.segment form { display: flex; margin: 0; }
.segment form button { flex: 1 0 auto; }
.segment > *:last-child button { border-right: 0; }
.segment .seg { display: inline-block; }
@keyframes seg-pulse { 50% { opacity: .55; } }

/* ── Stat tiles, key–value ──────────────────────────────────────────── */

.stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.stat .k {
  font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-3);
}
.stat .v {
  display: flex; align-items: center; gap: var(--s1);
  font-family: var(--sans-display);
  font-size: var(--t-2xl); font-weight: 700; letter-spacing: -.02em; line-height: 1.1;
}
.stat .v.sm { font-family: var(--sans); font-size: var(--t-md); font-weight: 600; letter-spacing: 0; }
.stat .note { font-size: var(--t-xs); color: var(--ink-3); }

.kv { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px var(--s2); margin: 0 0 var(--s3); }
.kv dt {
  align-self: baseline; padding-top: 2px;
  font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase;
  color: var(--ink-3);
}
.kv dd { margin: 0; font-size: var(--t-sm); overflow-wrap: anywhere; }
.kv.rows { grid-template-columns: minmax(0, 1fr); gap: 0; }
.kv.rows dt { padding-top: var(--s1); }
.kv.rows dd { padding-bottom: var(--s1); border-bottom: 1px solid var(--line-2); }

/* Legacy pages build .facts; it is the same instrument in dl form. */
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); margin-bottom: var(--s3); }
.facts > div { background: var(--bg); padding: var(--s2); min-width: 0; }
.facts dt { font-size: var(--t-micro); font-weight: 500; letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3); }
.facts dd { margin: var(--s-half) 0 0; font-size: var(--t-base); overflow-wrap: anywhere; }
.facts dd.big { font-family: var(--sans-display); font-size: var(--t-xl); font-weight: 700; letter-spacing: -.02em; }

/* ── Bands ──────────────────────────────────────────────────────────── */

.band {
  border: 1px solid var(--line); border-left-width: var(--spine);
  background: var(--bg-2);
  padding: var(--s2);
  margin: 0 0 var(--s3);
}
.band h3 { margin: 0 0 var(--s-half); font-size: var(--t-base); }
.band p { margin: 0; font-size: var(--t-sm); color: var(--ink-2); }
.band-body { font-size: var(--t-sm); color: var(--ink-2); }
.band-body p { margin: 0 0 var(--s-half); }
.band-body p:last-child { margin-bottom: 0; }
.band .band-actions { display: flex; flex-wrap: wrap; gap: var(--s1); margin-top: var(--s2); }
.band.down { border-left-color: var(--danger-ink); background: var(--bg-2); background: var(--danger-wash); }
.band.warn { border-left-color: var(--warn-ink); background: var(--bg-2); background: var(--warn-wash); }
.band.info { border-left-color: var(--info-ink); background: var(--bg-2); background: var(--info-wash); }
.band.ok { border-left-color: var(--ok-ink); background: var(--bg-2); background: var(--ok-wash); }

/* ── Delta ──────────────────────────────────────────────────────────── */

/* Direction is decided in the template, never by sign. An improvement is
   ink, not green: only a regression earns --warn, only a breach --danger. */
.delta { font-size: var(--t-xs); color: var(--ink-2); white-space: nowrap; font-variant-numeric: tabular-nums slashed-zero; }
.delta.worse { color: var(--warn-ink); }
.delta.breach { color: var(--danger-ink); }
.delta.flat { color: var(--ink-3); }

/* ── Chips / badges ─────────────────────────────────────────────────── */

.badge, .chip {
  display: inline-flex; align-items: center; gap: var(--s-half);
  border: 1px solid var(--line); padding: 1px var(--s1); margin: 1px var(--s-half) 1px 0;
  font-size: var(--t-xs); color: var(--ink-2); white-space: nowrap;
}
.badge b, .chip b { font-weight: 700; color: var(--ink); }
.badge.solid, .chip.solid { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.badge.solid b, .chip.solid b { color: var(--bg); }
.badge.bad, .chip.bad { border-color: var(--danger); color: var(--danger-ink); }
.badge.bad b, .chip.bad b { color: var(--danger-ink); }
a.chip:hover, a.badge:hover { border-color: var(--ink); color: var(--ink); }

/* ── Tables ─────────────────────────────────────────────────────────── */

/* The wrapper is the scroll container in both axes, so the sticky header
   offsets from the wrapper's top edge — never from the viewport. Tables
   shorter than the cap simply never scroll. */
.table-wrap {
  overflow: auto;
  max-height: calc(100vh - 13rem);
  border: 1px solid var(--line);
  margin: 0 0 var(--s3);
}
table { border-collapse: collapse; width: 100%; font-size: var(--t-sm); }
.table-wrap > table { margin: 0; }
table:not(.table-wrap > table) { border: 1px solid var(--line); margin: 0 0 var(--s3); }

th, td { padding: var(--s1) var(--s2); text-align: left; vertical-align: top; }
tbody td { height: var(--row-h); }
th {
  position: sticky; top: 0; z-index: 10;
  background: var(--bg-2);
  font-size: var(--t-xs); font-weight: 600; letter-spacing: 0; text-transform: none;
  color: var(--ink-2); white-space: nowrap;
  box-shadow: inset 0 -1px 0 var(--line);
}
tbody tr { border-top: 1px solid var(--line-2); }
tbody tr:hover { background: var(--bg-2); }
/* Every fifth row takes the heavier rule: the eye counts in fives. */
tbody tr:nth-child(5n) { border-top-color: var(--line); }
td.num { font-weight: 600; }
td.n, th.n { text-align: right; font-variant-numeric: tabular-nums slashed-zero; }
.nowrap { white-space: nowrap; }
td.bad, .bad { color: var(--danger-ink); }
tr.paused td { background: repeating-linear-gradient(135deg, var(--bg-2) 0 6px, var(--bg) 6px 12px); }

th button[data-sort] {
  display: inline-flex; align-items: center; gap: var(--s-half);
  border: 0; background: none; color: inherit; font: inherit; padding: 0; cursor: pointer;
}
th button[data-sort]:hover { background: none; color: var(--ink); }
th button[data-sort]::after { content: "\2195"; color: var(--ink-3); font-size: var(--t-micro); }
th[aria-sort="ascending"] button[data-sort]::after { content: "\2191"; color: var(--ink); }
th[aria-sort="descending"] button[data-sort]::after { content: "\2193"; color: var(--ink); }
tbody tr.on { background: var(--bg-3); }
tbody tr.on td:first-child { box-shadow: inset 2px 0 0 var(--ink); }

.sticky-col { position: sticky; left: 0; z-index: 5; background: var(--bg); border-right: 1px solid var(--line-3); }
tbody tr:hover .sticky-col { background: var(--bg-2); }
th.sticky-col { z-index: 11; background: var(--bg-2); }

/* ── Forms ──────────────────────────────────────────────────────────── */

label { display: block; margin: var(--s2) 0 var(--s-half); font-size: var(--t-xs); font-weight: 500; color: var(--ink-2); }
label.check { display: inline-flex; align-items: center; gap: var(--s1); margin: var(--s1) var(--s2) var(--s1) 0; font-size: var(--t-sm); color: var(--ink); }
label.check input { margin: 0; }

input, select, textarea {
  font: inherit; font-size: var(--t-base);
  color: var(--ink); background: var(--bg);
  border: 1px solid var(--line); border-radius: 0;
  padding: 6px var(--s1);
}
input:hover, select:hover, textarea:hover { border-color: var(--ink-3); }
input[type="text"], input[type="password"], input[type="number"], input[type="search"], input:not([type]), select, textarea { width: min(100%, 40rem); }
input[type="checkbox"] { width: auto; accent-color: var(--ink); }
textarea { min-height: 5rem; font-family: var(--mono); font-size: var(--t-sm); }
::placeholder { color: var(--ink-3); opacity: 1; }

button, .btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s1);
  font: inherit; font-size: var(--t-sm); font-weight: 600;
  color: var(--ink); background: var(--bg);
  border: 1px solid var(--ink); border-radius: 0;
  padding: 6px var(--s2);
  cursor: pointer;
}
button:hover, .btn:hover { color: var(--bg); background: var(--ink); }
button:disabled { border-color: var(--line); color: var(--ink-3); cursor: not-allowed; }
button:disabled:hover { background: var(--bg); color: var(--ink-3); }
button.quiet, .btn.quiet { border-color: var(--line); font-weight: 500; color: var(--ink-2); }
button.quiet:hover, .btn.quiet:hover { border-color: var(--ink); background: var(--ink); color: var(--bg); }
button.small, .btn.small { padding: 2px var(--s1); font-size: var(--t-xs); }
button.danger { border-color: var(--danger); color: var(--danger-ink); }
button.danger:hover { background: var(--danger); color: var(--bg); }

form.inline { display: inline; }
.row { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: flex-end; }
.row > * { flex: 0 1 auto; }
.row label { margin-top: 0; }
.actions { display: flex; flex-wrap: wrap; gap: var(--s-half); align-items: center; }

/* The verbs on one row. Above 900px they sit inline and the disclosure is
   not there at all; below it they fold into a full-width sheet (§4.5). */
.rowacts { border: 0; background: none; }
.rowacts > summary { display: none; }
.rowacts > .acts { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-half); }
.rowacts > .acts form { display: inline-flex; margin: 0; }
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: var(--s2); }

/* ── Blocks ─────────────────────────────────────────────────────────── */

.panel { border: 1px solid var(--line); padding: var(--s2); margin: 0 0 var(--s2); background: var(--bg); }
.panel h3 { margin-bottom: var(--s1); }

pre, .output {
  border: 1px solid var(--line); padding: var(--s2); margin: 0 0 var(--s2);
  white-space: pre-wrap; overflow: auto; max-height: 34rem;
  background: var(--bg-2); color: var(--ink);
  font-size: var(--t-sm); line-height: 1.5;
}

.empty {
  border: 1px dashed var(--line); padding: var(--s4) var(--s3);
  color: var(--ink-2); background: var(--bg-2);
}
.empty strong { display: block; margin-bottom: var(--s-half); font-size: var(--t-base); color: var(--ink); }
.empty p { margin: 0 0 var(--s2); font-size: var(--t-sm); color: var(--ink-3); max-width: 34rem; }
.empty p:last-child { margin-bottom: 0; }
.empty.pending { border-style: solid; border-color: var(--line-2); background: none; padding: var(--s3); }
.empty.bare { border: 0; background: none; padding: var(--s3) var(--s2); }

.drop { border: 1px solid var(--line); background: var(--bg); }
.drop > summary {
  padding: var(--s1) var(--s2);
  font-size: var(--t-sm); font-weight: 600; color: var(--ink-2);
  cursor: pointer; list-style: none;
}
.drop > summary::-webkit-details-marker { display: none; }
.drop > summary::before { content: "+ "; color: var(--ink-3); }
.drop[open] > summary { border-bottom: 1px solid var(--line); color: var(--ink); }
.drop[open] > summary::before { content: "\2212 "; }
.drop > summary:hover { color: var(--ink); }
.drop-body { padding: var(--s2); }

.error {
  border: 1px solid var(--danger); border-left-width: var(--spine);
  padding: var(--s2); color: var(--danger-ink); font-weight: 500; background: var(--bg);
}
.mark.error {
  position: relative; display: inline-block;
  width: var(--mark); height: var(--mark); padding: 0;
  border: 1px solid currentColor; border-left-width: 1px;
  color: var(--danger-ink); background: currentColor; font-weight: 400;
}

.sev { display: inline-flex; gap: var(--s1); }
.sev .s {
  display: inline-flex; align-items: center; gap: 3px;
  font-variant-numeric: tabular-nums slashed-zero;
}
.lh { display: inline-flex; flex-wrap: wrap; gap: var(--s-half) var(--s2); }
.runform { margin: 0 0 var(--s-half); }

.hint { font-size: var(--t-xs); color: var(--ink-3); margin: var(--s-half) 0 0; }

.offscreen { position: fixed; left: -9999px; top: 0; opacity: 0; }
.narrow { max-width: 26rem; }
.medium { max-width: 44rem; }

/* A timeline of stamped events — alerts, incidents, commands. */
.timeline { list-style: none; margin: 0 0 var(--s3); padding: 0; border-top: 1px solid var(--line-2); }
.timeline li {
  display: grid; grid-template-columns: var(--gutter) minmax(0, 1fr) max-content;
  gap: var(--s1); padding: var(--s1) 0; border-bottom: 1px solid var(--line-2); align-items: baseline;
}
.timeline .what { min-width: 0; overflow-wrap: anywhere; }
.timeline .what .sub { display: block; font-size: var(--t-xs); color: var(--ink-3); }
.timeline .when { font-size: var(--t-xs); color: var(--ink-3); white-space: nowrap; }

/* A ranked list: label left, number right. */
.ranked { list-style: none; margin: 0 0 var(--s3); padding: 0; }
.ranked li {
  display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: var(--s2);
  padding: var(--s-half) 0; border-bottom: 1px solid var(--line-2); font-size: var(--t-sm);
}
.ranked li .n { font-variant-numeric: tabular-nums slashed-zero; color: var(--ink-2); }

.tablist { display: flex; border-bottom: 1px solid var(--line); margin: 0 0 var(--s3); }
.tablist [role="tab"] {
  border: 0; border-bottom: 2px solid transparent; background: none; color: var(--ink-2);
  padding: var(--s1) var(--s2); font: inherit; font-size: var(--t-sm); font-weight: 500; cursor: pointer;
}
.tablist [role="tab"]:hover { color: var(--ink); background: var(--bg-2); }
.tablist [role="tab"][aria-selected="true"] { color: var(--ink); font-weight: 600; border-bottom-color: var(--ink); }

/* ── Dialogs, palette, toast ────────────────────────────────────────── */

dialog {
  border: 1px solid var(--ink); border-radius: 0;
  background: var(--bg); color: var(--ink);
  padding: 0;
  box-shadow: var(--shadow-lift);
}
dialog::backdrop { background: rgba(0, 0, 0, .35); }

.confirm { width: min(30rem, calc(100vw - var(--s4))); }
.confirm h2 { margin: 0; padding: var(--s2); border-bottom: 1px solid var(--line); font-size: var(--t-base); }
.confirm h2::after { content: none; }
.confirm p { margin: 0; padding: var(--s2); font-size: var(--t-sm); color: var(--ink-2); }
.confirm .confirm-actions { display: flex; justify-content: flex-end; gap: var(--s1); padding: var(--s2); border-top: 1px solid var(--line); }

.palette { width: min(38rem, calc(100vw - var(--s4))); margin: 12vh auto auto; padding: 0; }
.palette input {
  width: 100%; border: 0; border-bottom: 1px solid var(--line); padding: var(--s2);
  font-size: var(--t-md); background: none;
}
.palette input:focus-visible { outline: none; border-bottom-color: var(--ink); }
.palette ul { list-style: none; margin: 0; padding: 0; max-height: 52vh; overflow-y: auto; }
.palette li { border-bottom: 1px solid var(--line-2); }
.palette li:last-child { border-bottom: 0; }
.palette a { display: flex; align-items: baseline; gap: var(--s2); padding: var(--s1) var(--s2); font-size: var(--t-sm); color: var(--ink-2); }
.palette a .where { margin-left: auto; font-size: var(--t-xs); color: var(--ink-3); }
.palette li.on a, .palette a:hover { background: var(--ink); color: var(--bg); }
.palette li.on a .where, .palette a:hover .where { color: var(--bg); opacity: .7; }
.palette .foot { display: flex; gap: var(--s2); padding: var(--s1) var(--s2); border-top: 1px solid var(--line); font-size: var(--t-xs); color: var(--ink-3); }
.palette .none { padding: var(--s3) var(--s2); font-size: var(--t-sm); color: var(--ink-3); }

.toast {
  position: fixed; right: var(--s3); bottom: var(--s3); z-index: 70;
  max-width: 26rem; padding: var(--s1) var(--s2);
  background: var(--ink); color: var(--bg);
  font-size: var(--t-sm);
  box-shadow: var(--shadow-lift);
}
.toast.bad { background: var(--danger); }

/* ── Responsive ─────────────────────────────────────────────────────── */

/* 1280 — the aside gives up and becomes a strip; optional columns go. */
@media (max-width: 1280px) {
  .split { grid-template-columns: minmax(0, 1fr); gap: var(--s3); }
  .aside { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3); }
  .aside > * + * { margin-top: 0; }
  /* Secondary columns yield before the 900px stacked-record transform. */
  .col-opt { display: none; }
  .sheet > .span-8, .sheet > .span-6, .sheet > .span-4 { grid-column: span 12; }
}

/* 1100 — a sticky rail plus a sticky thead plus a capped table is three
   scrollbars in one band. The cap goes first. */
@media (max-width: 1100px) {
  .table-wrap { max-height: none; }
  .b5 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .b4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  :root { --row-h: 36px; }
  .shell { grid-template-columns: minmax(0, 1fr); grid-template-rows: var(--bar) auto minmax(0, 1fr); }
  .brand { width: auto; flex: 0 0 auto; border-right: 1px solid var(--line); }
  .rail {
    position: static; height: auto; display: flex; align-items: stretch; overflow-x: auto; padding: 0;
    border-right: 0; border-bottom: 1px solid var(--line);
  }
  /* Mobile fix 1 (§4.5). Eight entries fit one line at 375px, so the strip
     hides its scrollbar rather than spend 12px of a 36px band on it — and
     says so with a visible edge fade, which is what a hidden scrollbar owes
     the reader. Same treatment for the readout above it. */
  .rail { scrollbar-width: none; -ms-overflow-style: none; }
  .rail::-webkit-scrollbar { display: none; }
  .rail {
    -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 20px), transparent 100%);
    mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 20px), transparent 100%);
  }
  .rail-group { display: flex; align-items: stretch; border-right: 1px solid var(--line); }
  .rail-group:last-child { border-right: 0; }
  .rail a { border-left: 0; border-bottom: 2px solid transparent; white-space: nowrap; }
  .rail a[aria-current="page"] { border-left-color: transparent; }
  .rail-label { display: none; }
  /* Mobile fix 2 (§4.5). Two numbers mean act now; the rest is a Ctrl-K away
     and on the page it belongs to. Measured at 390px: the top bar is 390px
     wide and the brand plus the end controls were taking all of it, so the
     two readings that survive had nowhere to sit. Everything in that bar
     that repeats something already on screen gives up its width first —
     the brand's sub-label, the shortcut hint, the theme toggle's word, and
     the Alerts link, which the readout and the rail pip both already say. */
  .gauge { display: none; }
  .gauge.g-down, .gauge.g-alerts { display: flex; min-width: 0; }
  .brand span { display: none; }
  .palette-open kbd { display: none; }
  .theme-toggle span { display: none; }
  .alerts-link { display: none; }
  .topbar-end { gap: var(--s-half); padding: 0 var(--s1); }
  .pagehead, .filterbar { position: static; }
  .aside { grid-template-columns: minmax(0, 1fr); }
  main { padding: var(--s2) var(--s2) var(--s5); }
  .b5, .b4, .b3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tape-row { grid-template-columns: 6rem minmax(0, 1fr); }
  .tape-row .v { grid-column: 1 / -1; }

  /* Mobile fix 4 (§4.5). A thumb needs 40px; a row action needs 44. */
  button, .btn { min-height: 40px; }
  button.small, .btn.small { min-height: 40px; padding: var(--s1) var(--s2); font-size: var(--t-sm); }

  /* Mobile fix 3 (§4.5). Open / Start / Stop / Restart / Logs four pixels
     apart is how the wrong client Site gets stopped from the couch. Below
     900px the row's verbs fold behind one 44px Actions button and open as a
     full-width sheet, destructive last and ruled off. <details> does it with
     no script, so the sheet works before app.js loads and app.js already
     restores an open <details> across a refresh. */
  .rowacts > summary {
    display: flex; align-items: center; justify-content: space-between;
    min-height: 44px; padding: 0 var(--s2); margin: var(--s-half) 0 0;
    border: 1px solid var(--ink);
    font-size: var(--t-sm); font-weight: 600; color: var(--ink);
    cursor: pointer; list-style: none;
  }
  .rowacts > summary::-webkit-details-marker { display: none; }
  .rowacts > summary::after { content: "\25BE"; color: var(--ink-3); }
  .rowacts[open] > summary::after { content: "\25B4"; }
  /* Full width of the stacked record, so the sheet is a sheet and not a
     button-sized column at the end of a label. */
  .rows-stack td .rowacts { flex: 1 1 auto; min-width: 0; }
  .rowacts > .acts { display: block; border: 1px solid var(--line); border-top: 0; }
  .rowacts > .acts > * { display: block; width: 100%; margin: 0; }
  .rowacts > .acts form { display: block; }
  .rowacts > .acts button, .rowacts > .acts .btn {
    width: 100%; min-height: 44px; justify-content: flex-start;
    border: 0; border-bottom: 1px solid var(--line-2);
  }
  .rowacts > .acts .destructive { border-top: var(--spine) solid var(--line); }
  .rowacts > .acts .destructive button { color: var(--danger-ink); }

  /* Tables become stacked records: one bordered block per row, its state on
     the spine, each cell labelled from data-label. */
  .rows-stack thead { display: none; }
  .rows-stack, .rows-stack tbody, .rows-stack tr, .rows-stack td { display: block; width: auto; }
  .rows-stack tr { border: 1px solid var(--line); border-left-width: var(--spine); margin: 0 0 var(--s1); padding: var(--s1); }
  /* Deliberate: stacked records restore .col-opt because they scroll down,
     not across. This more-specific td rule must outrank .col-opt. */
  .rows-stack td { display: flex; gap: var(--s2); padding: 2px 0; height: auto; text-align: left; }
  .rows-stack td::before {
    content: attr(data-label); flex: 0 0 8rem;
    font-size: var(--t-micro); letter-spacing: var(--track); text-transform: uppercase; color: var(--ink-3);
  }
}

@media (max-width: 600px) {
  .bank, .b5, .b4, .b3, .b2 { grid-template-columns: minmax(0, 1fr); }
  .filterbar form { margin-left: 0; width: 100%; }
  .filterbar input[type="search"] { width: 100%; }
}

/* 430 / 390 — the two phone widths this panel is actually read at.
   Nothing here changes the design; it removes the last four ways a page
   pushed the document sideways. Verified by walking every page at both
   widths: the only horizontal scroll left is INSIDE a .table-wrap or a
   <pre>, which is where it belongs. */
@media (max-width: 430px) {
  html, body { max-width: 100%; overflow-x: clip; }
  .shell { max-width: 100%; }
  main { padding: var(--s2) var(--s1) var(--s5); }
  /* A hostname, a docroot or a SANs list is one unbreakable token. */
  .mono, code, .kv dd, .facts dd, td, .stat .v, .timeline .what { overflow-wrap: anywhere; }
  .stat .v { font-size: var(--t-xl); }
  /* Full-width fields instead of a 40rem box the viewport cannot hold. */
  input[type="text"], input[type="password"], input[type="number"],
  input[type="search"], input:not([type]), select, textarea { width: 100%; }
  .row { display: block; }
  .row > * + * { margin-top: var(--s1); }
  .grid-2 { grid-template-columns: minmax(0, 1fr); }
  .facts { grid-template-columns: minmax(0, 1fr); }
  .chain .link { flex: 1 1 100%; border-right: 0; border-bottom: 1px solid var(--line); }
  .chain .link:last-child { border-bottom: 0; }
  .segment { display: flex; width: 100%; }
  .rows-stack td::before { flex-basis: 6.5rem; }
  .pagehead { flex-wrap: wrap; }
  .pagehead-actions { width: 100%; }
  .terminal, .output, pre { max-width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; animation-iteration-count: 1 !important; }
}

/* ── Print ──────────────────────────────────────────────────────────── */

@media print {
  .topbar, .rail, .skip, .filterbar form, .pagehead-actions, .toast, dialog { display: none !important; }
  .shell { display: block; }
  main { padding: 0; }
  .pagehead { position: static; }
  .table-wrap { max-height: none; overflow: visible; border-color: var(--line-3); }
  th { position: static; }
  a { text-decoration: none; }
  .band, .bank, .panel, .instr { break-inside: avoid; }
}

/* Alerts — a failure line must stay readable: the payload preview wraps inside
   its cell instead of stretching the table sideways. Greyscale only. */
.alert-detail { max-width: 52ch; overflow-wrap: anywhere; margin-top: var(--s-half); }
.alert-detail:empty { display: none; }

/* The only [data-pct] step table in the panel: /forge writes these from the
   live poll, and JS may set an attribute but never a style. */
.meter { display: block; height: var(--tape-h); border: 1px solid var(--line-3); background: var(--bg-2); }
.meter i { display: block; height: 100%; width: 0; background: var(--ink-2); }
.meter[data-pct="0"] i { width: 0; }
.meter[data-pct="5"] i { width: 5%; }
.meter[data-pct="10"] i { width: 10%; }
.meter[data-pct="15"] i { width: 15%; }
.meter[data-pct="20"] i { width: 20%; }
.meter[data-pct="25"] i { width: 25%; }
.meter[data-pct="30"] i { width: 30%; }
.meter[data-pct="35"] i { width: 35%; }
.meter[data-pct="40"] i { width: 40%; }
.meter[data-pct="45"] i { width: 45%; }
.meter[data-pct="50"] i { width: 50%; }
.meter[data-pct="55"] i { width: 55%; }
.meter[data-pct="60"] i { width: 60%; }
.meter[data-pct="65"] i { width: 65%; }
.meter[data-pct="70"] i { width: 70%; }
.meter[data-pct="75"] i { width: 75%; }
.meter[data-pct="80"] i { width: 80%; }
.meter[data-pct="85"] i { width: 85%; }
.meter[data-pct="90"] i { width: 90%; }
.meter[data-pct="95"] i { width: 95%; }
.meter[data-pct="100"] i { width: 100%; }
