/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   monitor.odooclick.com — design system

   Dark-first: this is a NOC board, most often opened on a phone at night.

   ONE COLOUR RULE, obeyed everywhere: HUE MEANS STATUS AND NOTHING ELSE.
   Links, the focus ring, search highlighting, chart ink, axes, rules and every piece of chrome are
   achromatic. Chart series use --ink, a neutral grey that means "measured, and not currently
   interesting" — a 71-day certificate is not healthy, it is irrelevant, and painting it green is
   how a board becomes a reassuring wall of green. After this rule, seeing a colour on this page
   means exactly one thing is true about a server.

   (The shipped stylesheet used --stale #58a6ff for BOTH a stale host and every hyperlink and focus
   ring. That is one colour carrying three meanings on a status board. Fixed here.)

   SHAPE LANGUAGE, so the board survives greyscale and colour-blindness:
     an ASSERTION about state is always a rounded pill/mark carrying a glyph  (✓ ▲ ⚠ ✕ ? ↻)
     a MEASUREMENT is always a squared rectangle sitting on a baseline
   Status additionally carries a distinct left-rule weight AND line-style, and unknown/stale carry
   a 135° hatch so "we have no reading here" is a texture, not calm empty space.

   Every status token below was contrast-checked against --bg and --surface; the ratios are
   computed, not estimated, and all clear WCAG AA for normal text.
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */

:root {
  /* ground */
  --bg:        #0d1117;
  --surface:   #161b22;
  --surface-2: #1c2128;
  --surface-3: #21262d;
  --border:    #30363d;
  --border-2:  #3d444d;
  --grid:      #262c34;   /* chart rules, tracks, row separators */

  /* text */
  --text:  #e6edf3;       /* 16.02:1 on bg */
  --muted: #8b949e;       /*  6.15:1 on bg */
  --dim:   #6e7681;       /*  4.61:1 — units, axis ticks, inactive counts. NEVER a value. */
  --ink:   #7d8590;       /*  5.07:1 — chart series: measured, not interesting. Not green. */

  /* status — the only hues on the page */
  --ok:      #3fb950;     /* 7.45:1 */
  --warn:    #d29922;     /* 7.50:1 */
  --crit:    #f85149;     /* 5.65:1 */
  --down:    #ff7b72;     /* 7.51:1 */
  --unknown: #a371f7;     /* 5.64:1 */
  --stale:   #58a6ff;     /* 7.49:1 */
  --maint:   #8b949e;
  /* four operator-facing severity bands (see .ribbon). All pass AA on --bg and --surface:
     red 7.51 · orange 7.48 · yellow 12.24 · green 7.45 */
  --sev-red:    #ff7b72;
  --sev-orange: #f0883e;
  --sev-yellow: #f2cc60;
  --sev-green:  #3fb950;

  /* chart zone washes — non-text, so low alpha is legal */
  --tint-crit: rgba(248, 81, 73, .16);
  --tint-warn: rgba(210, 153, 34, .14);
  --tint-ink:  rgba(125, 133, 144, .20);
  --tint-row:  rgba(163, 113, 247, .07);   /* unknown/stale hatch */

  /* achromatic /24 identity bands — an address band can never be misread as a status */
  --net-a: #b6c0cb;  --net-b: #9aa4ae;  --net-c: #7d8590;  --net-d: #6e7681;

  --focus: #e6edf3;       /* 16:1, achromatic: collides with no status */

  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;
  --r-sm: 6px; --r: 8px; --r-lg: 12px; --r-pill: 999px;
  --tap: 44px;

  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "JetBrains Mono", Menlo, Consolas, monospace;

  --shadow: 0 1px 2px rgba(0,0,0,.5), 0 8px 24px rgba(0,0,0,.32);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #ffffff; --surface: #f6f8fa; --surface-2: #eef1f4; --surface-3: #e3e7eb;
    --border: #d0d7de; --border-2: #afb8c1; --grid: #e2e6ea;
    --text: #1f2328; --muted: #59636e; --dim: #6e7781; --ink: #6e7781;
    --ok: #1a7f37; --warn: #9a6700; --crit: #cf222e; --down: #a40e26;
    --unknown: #8250df; --stale: #0969da; --maint: #59636e;
    --sev-red: #a40e26; --sev-orange: #8a4b00; --sev-yellow: #7a5c00; --sev-green: #1a7f37;
    --tint-crit: rgba(207, 34, 46, .12);
    --tint-warn: rgba(154, 103, 0, .12);
    --tint-ink:  rgba(110, 119, 129, .16);
    --tint-row:  rgba(130, 80, 223, .07);
    --net-a: #57606a; --net-b: #6e7781; --net-c: #848d97; --net-d: #6e7781;
    --focus: #1f2328;
    --shadow: 0 1px 2px rgba(31,35,40,.10), 0 8px 24px rgba(31,35,40,.10);
  }
}

*, *::before, *::after { box-sizing: border-box; }

/* display:grid must never beat [hidden] */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.5 var(--font);
  padding-bottom: env(safe-area-inset-bottom);
  overflow-x: hidden;            /* belt and braces: the page NEVER scrolls sideways */
}

/* links are achromatic and underlined — blue is a host state, not a hyperlink */
a { color: var(--text); text-decoration-color: var(--border-2); text-underline-offset: 2px; }
a:hover { text-decoration-color: var(--text); }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 60;
  background: var(--surface); border: 1px solid var(--border);
  padding: var(--s3) var(--s4); border-radius: 0 0 var(--r) 0;
}
.skip:focus { left: 0; }

.mono { font-family: var(--mono); }
.small { font-size: .8125rem; }
.muted { color: var(--muted); }
.num, .m, .cn, .barn, .railn { font-variant-numeric: tabular-nums; }
.ok-t { color: var(--ok); } .warn-t { color: var(--warn); } .crit-t { color: var(--crit); }
.v-warn { color: var(--warn); } .v-crit { color: var(--crit); }

main { max-width: 1440px; margin: 0 auto; padding: 0 var(--s4) var(--s7); }
.lbspacer { flex: 1 1 auto; }

/* ── icons: one family, 20px grid, stroked, currentColor ─────────────────────────────────── */
.ico { width: 20px; height: 20px; flex: none; fill: none; stroke: currentColor;
       stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.ico .fill { fill: currentColor; stroke: none; }
.ico-brand { stroke-width: 1.4; }
.ico-brand rect { fill: none; } .ico-brand circle { fill: currentColor; stroke: none; }

/* ═══ header ══════════════════════════════════════════════════════════════════════════════ */
.top {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s4);
  padding-top: max(var(--s2), env(safe-area-inset-top));
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
@supports (backdrop-filter: blur(12px)) {
  .top { background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(12px); }
}
.brand {
  display: inline-flex; align-items: center; gap: var(--s2);
  min-height: var(--tap); padding: 0 var(--s1);
  text-decoration: none; color: var(--text); font-weight: 640; letter-spacing: -.01em;
}
.brand-t { font-size: .9375rem; }
@media (max-width: 439px) { .brand-t { display: none; } }

.find { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.find .ico-find { position: absolute; left: 11px; top: 50%; margin-top: -10px;
                  color: var(--muted); pointer-events: none; stroke-width: 1.8; }
.find input {
  width: 100%; min-width: 0; min-height: var(--tap);
  padding: 0 40px 0 38px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-pill);
  font: inherit; font-size: 1rem;      /* 16px — anything smaller makes iOS zoom on focus */
}
.find input::placeholder { color: var(--dim); }
.find input:focus { border-color: var(--border-2); }
.find input::-webkit-search-cancel-button { display: none; }
.qclear {
  position: absolute; right: 2px; top: 50%; margin-top: -22px;
  width: var(--tap); height: var(--tap);
  display: grid; place-items: center;
  background: none; border: 0; color: var(--muted); cursor: pointer; border-radius: var(--r-pill);
}
.qclear:hover { color: var(--text); }

.pulse {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--tap); padding: 0 var(--s2);
  text-decoration: none; color: var(--muted);
  font: 600 .75rem/1 var(--mono); font-variant-numeric: tabular-nums;
}
.pulse .dot { width: 7px; height: 7px; border-radius: var(--r-pill); background: var(--ok);
              animation: breathe 4s ease-in-out infinite; }
.pulse-lost { color: var(--crit); }
.pulse-lost .dot { background: var(--crit); animation: none; }
@media (max-width: 519px) { .pulse .pt { display: none; } }

.iconbtn {
  min-width: var(--tap); min-height: var(--tap);
  display: inline-grid; place-items: center;
  background: none; border: 1px solid transparent; border-radius: var(--r);
  color: var(--muted); cursor: pointer;
}
.iconbtn:hover { color: var(--text); background: var(--surface); border-color: var(--border); }
.iconbtn[aria-pressed="true"] { color: var(--text); background: var(--surface-3); border-color: var(--border-2); }
.outform { margin: 0; display: flex; }
.linkbtn { background: none; border: 0; color: var(--text); font: inherit; font-size: .8125rem;
           cursor: pointer; text-decoration: underline; text-decoration-color: var(--border-2);
           min-height: var(--tap); padding: 0 var(--s2); }

/* ═══ 1 · verdict ═════════════════════════════════════════════════════════════════════════ */
.verdict { padding: var(--s5) 0 var(--s2); }
.vrule { display: block; width: 120px; height: 3px; margin-bottom: var(--s3); }
.vrule svg { display: block; width: 100%; height: 3px; }
.vrule rect { fill: var(--ok); }
.v-warn .vrule rect { fill: var(--warn); }
.v-crit .vrule rect, .v-down .vrule rect { fill: var(--crit); }
.v-unknown .vrule rect { fill: var(--unknown); }

/* One line, not a poster. The expert reads it in a saccade and moves on; at 2am it is the first
   thing rendered and it is words, which survive adrenaline better than glyphs. */
.vline {
  margin: 0; font-size: clamp(1.25rem, 5.2vw, 1.5rem); line-height: 1.25;
  font-weight: 650; letter-spacing: -.022em; text-wrap: balance;
  font-variant-numeric: tabular-nums;
}
.v-crit .vline, .v-down .vline { color: var(--down); }
.v-warn .vline { color: var(--warn); }
.v-unknown .vline { color: var(--unknown); }
.vsub { margin: var(--s1) 0 0; font-size: .875rem; color: var(--muted); }
.vsub b { color: var(--text); font-weight: 600; }

.sweepline {
  margin: 0 0 var(--s4); padding: var(--s2) var(--s3);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  font-size: .8125rem; color: var(--muted); font-variant-numeric: tabular-nums;
}
.sweepline b { color: var(--text); }
.sweepline.bad { border-color: var(--crit); border-left: 4px solid var(--crit); color: var(--text); }
.lostb { color: var(--crit); }

/* Feed lost is a property of the PAGE, not a badge on it — but it is signalled with a frame, a
   banner and desaturated GRAPHICS. Never with opacity on text: the moment the data is stale is the
   moment the operator most needs to read it. */
.board-stale { box-shadow: inset 0 0 0 3px var(--crit); }
.board-stale .chart, .board-stale .spark, .board-stale .bar, .board-stale .rail-svg { opacity: .45; }
.board-stale .figs::before {
  content: "Values below were last refreshed by the sweep named above. They are not live.";
  display: block; margin-bottom: var(--s3); padding: var(--s2) var(--s3);
  border: 1px dashed var(--crit); border-radius: var(--r);
  color: var(--crit); font-size: .8125rem;
}

/* ═══ 2 · ribbon ══════════════════════════════════════════════════════════════════════════ */
.lbl {
  display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
  margin: 0 0 var(--s2);
  font-size: .75rem; font-weight: 650; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted);
}
.lblx { font-weight: 400; letter-spacing: .02em; text-transform: none; color: var(--dim); }
.lblc { font-weight: 600; letter-spacing: .02em; text-transform: none; color: var(--text);
        font-variant-numeric: tabular-nums; }

.ribbonwrap { margin-bottom: var(--s5); }

/* ── the fleet, one server per host ──────────────────────────────────────────────────────────
   Four severity bands, as asked: red = down / urgent, orange = severity 1, yellow = severity 2,
   green = working fine. The chassis silhouette is IDENTICAL at every severity so the eye compares
   like with like and only the colour changes.

   Orange and yellow separate by just 1.64:1, which is below anything a colour-blind reader can be
   asked to rely on — so severity is carried on THREE channels: hue, the glyph badge, and the
   power-LED treatment (filled / ring / hollow / crossed). Kill the colour and it still reads.     */
.ribbon {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(58px, 1fr));
  gap: var(--s2) 4px;
}
.mk {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 4px 2px 5px;
  min-height: var(--tap);
  text-decoration: none; border-radius: var(--r-sm);
  color: var(--muted);
}
.mk:hover { background: var(--surface); }
.mk:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

.srv { width: 26px; height: 34px; display: block; flex: none; }
.srv-body  { fill: none; stroke: currentColor; stroke-width: 1.6; }
.srv-slot  { fill: currentColor; opacity: .85; }
.srv-pwr   { fill: none; stroke: currentColor; stroke-width: 1.5; }
.srv-pwr-i { stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }

/* colour + LED treatment per band. Each band publishes --sev so the badge can
   paint from it; currentColor would resolve to the badge's own color (the page
   background, set for the glyph) and render the fallback invisible. */
.sv-green  { --sev: var(--sev-green); color: var(--sev);  }
.sv-green  .srv-pwr { fill: var(--sev-green); fill-opacity: .9; }   /* solid LED = running */
.sv-green  .srv-pwr-i { stroke: var(--bg); }

.sv-yellow { --sev: var(--sev-yellow); color: var(--sev);  }
.sv-yellow .srv-pwr { fill: none; stroke-dasharray: 3 2.4; }        /* broken ring = not measured */

.sv-orange { --sev: var(--sev-orange); color: var(--sev);  }
.sv-orange .srv-pwr { fill: none; }                                  /* hollow ring = degraded */
.sv-orange .srv-slot { opacity: 1; }

.sv-red    { --sev: var(--sev-red); color: var(--sev);  }
.sv-red    .srv-body { stroke-width: 2.2; }
.sv-red    .srv-pwr  { fill: none; opacity: .5; }
.sv-red    .srv-slot { opacity: .45; }                               /* slots dark = not serving */

.sv-grey   { --sev: var(--maint); color: var(--sev); opacity: .75;  }

/* the badge is the channel that survives greyscale — never decorative */
.mkbadge {
  position: absolute; top: 0; right: 4px;
  min-width: 15px; height: 15px; padding: 0 2px;
  display: grid; place-items: center;
  border-radius: 999px; background: var(--sev, var(--muted));
  font: 700 10px/1 var(--mono); color: var(--bg);
}
.mkname {
  max-width: 100%; font-size: .625rem; line-height: 1.15; text-align: center;
  color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mk:hover .mkname, .mk:focus-visible .mkname { color: var(--text); }
/* a host in trouble names itself at full contrast even before you look at the colour */
.sv-red .mkname, .sv-orange .mkname { color: var(--text); font-weight: 600; }

/* the ribbon and the list must never disagree about what is in scope */
.mk.dim { opacity: .22; }

@media (max-width: 400px) {
  .ribbon { grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); }
  .srv { width: 22px; height: 29px; }
  .mkname { font-size: .5625rem; }
}
@media (prefers-reduced-motion: reduce) { .srv, .mk { transition: none; } }

.peek {
  margin: var(--s2) 0 0; min-height: 20px;
  font-size: .75rem; color: var(--muted); font-variant-numeric: tabular-nums;
}

.counts { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); }
.cbtn {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--tap); padding: 0 var(--s3);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-pill);
  color: var(--muted); font: inherit; font-size: .8125rem; cursor: pointer;
}
.cbtn:hover { border-color: var(--border-2); }
.cbtn .cg { font-family: var(--mono); font-weight: 700; }
.cbtn .cn { color: var(--text); font-weight: 650; font-variant-numeric: tabular-nums; }
.cbtn .cl { text-transform: uppercase; letter-spacing: .05em; font-size: .6875rem; }
.c-down    { color: var(--down); }    .c-crit  { color: var(--crit); }
.c-warn    { color: var(--warn); }    .c-ok    { color: var(--ok); }
.c-unknown { color: var(--unknown); } .c-stale { color: var(--stale); }
.c-maintenance { color: var(--maint); }
/* A zero count is still RENDERED: "we checked and there are none" is not "we did not check". */
.cbtn.zero { color: var(--dim); }
.cbtn.zero .cn { color: var(--dim); }
.cbtn[aria-pressed="true"] { background: var(--surface-3); border-color: currentColor; }

/* ═══ 3 · attention ═══════════════════════════════════════════════════════════════════════ */
.attention { margin-bottom: var(--s5); }
.lbl-soon { margin-top: var(--s5); }
.calm {
  margin: 0; padding: var(--s4);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  color: var(--muted); font-size: .9375rem;
}

.advice { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.advlink {
  display: grid; grid-template-columns: 18px minmax(0,1fr) auto; gap: var(--s2);
  align-items: center; min-height: var(--tap); padding: var(--s2) var(--s3);
  background: var(--surface); border: 1px solid var(--border);
  border-left: 4px solid var(--warn); border-radius: var(--r);
  text-decoration: none; color: var(--text);
}
.adv-unknown .advlink, .adv-stale .advlink { border-left-style: dashed; border-left-color: var(--unknown); }
.advlink:hover { background: var(--surface-2); }
.advg { font: 700 13px/1 var(--mono); color: var(--warn); text-align: center; }
.adv-unknown .advg, .adv-stale .advg { color: var(--unknown); }
.advmain { min-width: 0; }
.advtop { display: block; font-size: .9375rem; font-weight: 600; }
.advwhat { display: block; font-size: .8125rem; color: var(--muted); }
.advaddr { font: .75rem/1 var(--mono); color: var(--text); white-space: nowrap;
           overflow: hidden; text-overflow: ellipsis; max-width: 36vw; }
@media (max-width: 479px) { .advaddr { display: none; } }

/* ═══ 4 · figures ═════════════════════════════════════════════════════════════════════════ */
.figs { margin-bottom: var(--s5); }
.escalator {
  margin: 0 0 var(--s3); padding: var(--s2) var(--s3);
  border: 1px dashed var(--border-2); border-radius: var(--r);
  font-size: .8125rem; color: var(--muted);
}
.figgrid { display: grid; gap: var(--s3); }
@media (min-width: 1000px) { .figgrid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1360px) { .figgrid { grid-template-columns: 1fr 1fr 1fr; } }

.fig {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
  overflow: hidden;
}
.fig > summary {
  display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center;
  gap: var(--s1) var(--s2); padding: var(--s3) var(--s4);
  min-height: var(--tap); cursor: pointer; list-style: none;
}
.fig > summary::-webkit-details-marker { display: none; }
.fig > summary:hover { background: var(--surface-2); }
/* The SUMMARY carries the real number, so a fully folded deck still reports six sentences. */
.figt { grid-column: 1; font-size: .75rem; font-weight: 650; text-transform: uppercase;
        letter-spacing: .09em; color: var(--muted); }
.figsum { grid-column: 1; font-size: .9375rem; color: var(--text); font-variant-numeric: tabular-nums; }
.figsum b { font-weight: 650; }
.caret { grid-column: 2; grid-row: 1 / -1; width: 16px; height: 16px; color: var(--muted);
         transition: transform .18s ease; align-self: center; }
.fig[open] > summary .caret { transform: rotate(90deg); }
.agechip { grid-column: 1; }

.figbody { padding: 0 var(--s4) var(--s4); }
.chart { display: block; width: 100%; height: auto; margin-bottom: var(--s1); }
.chart-strip { margin-top: var(--s2); }

/* chart ink — achromatic. Colour appears only where severity is genuinely asserted. */
.axis, .tick, .grid { stroke: var(--grid); stroke-width: 1; }
.tl { fill: var(--dim); font: 9px var(--mono); }
.zlabel { fill: var(--dim); font: 8.5px var(--font); }
.zone.z-crit { fill: var(--tint-crit); }
.zone.z-warn { fill: var(--tint-warn); }
.dot { stroke: var(--bg); stroke-width: 1.2; }
.d-calm { fill: var(--ink); }
.d-warn { fill: var(--warn); }
.d-crit { fill: var(--crit); }
.track { fill: var(--grid); }
.track-none { fill: url(#p-unknown); stroke: var(--border-2); stroke-width: .8; }
.ldf { fill: var(--ink); }
.ldf-hot { fill: var(--warn); }
.sq { fill: var(--ink); }
.sq-hot { fill: var(--warn); }
.ll { fill: var(--text); font: 650 10px var(--font); }
.llm { fill: var(--dim); font: 9px var(--mono); }
.lr { fill: var(--muted); font: 9.5px var(--font); }
.lv { fill: var(--muted); font: 650 9.5px var(--mono); }
.lv-hot { fill: var(--warn); }
.lv-none { fill: var(--dim); font-weight: 400; }
.line { stroke: var(--ink); stroke-width: 1.75; stroke-linejoin: round; stroke-linecap: round; }
.area { fill: var(--tint-ink); }
.latgap { fill: url(#p-gap); }
.clipmark { fill: var(--warn); }
.ab-ok   { fill: var(--ink); }
.ab-warn { fill: var(--warn); }
.ab-down { fill: var(--down); }
.ab-gap  { fill: url(#p-gap); }
.pt-bg { fill: none; }
.pt-unknown { stroke: var(--unknown); stroke-width: 2.4; }
.pt-gap { stroke: var(--down); stroke-width: 2; }

.legend { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3); margin: var(--s1) 0 0;
          font-size: .6875rem; color: var(--muted); }
.lg { display: inline-flex; align-items: center; gap: 5px; }
.lgs { width: 18px; height: 8px; flex: none; }
.lg-hot { color: var(--warn); }
.sw { width: 14px; height: 8px; border-radius: 2px; background: var(--ink); display: inline-block; }
.sw.ab-warn { background: var(--warn); } .sw.ab-down { background: var(--down); }
.sw.ab-gap { background: repeating-linear-gradient(135deg, var(--down) 0 2px, transparent 2px 5px); }

.fignote { margin: var(--s2) 0 0; font-size: .8125rem; color: var(--muted); }
.fignote a { color: var(--text); }
.fignote-na { color: var(--unknown); }
.fignote-na a { color: var(--unknown); }

.figq { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); }
/* Every statistic is also the query that lists the hosts behind it. Clicking teaches the grammar. */
.qbtn {
  display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 var(--s3);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-pill);
  color: var(--text); font: inherit; font-size: .8125rem; cursor: pointer; text-decoration: none;
}
.qbtn:hover { background: var(--surface-3); border-color: var(--border-2); }
.qbtn-hot { border-color: var(--warn); color: var(--warn); }
.vsub .qbtn { min-height: 0; padding: 0; border: 0; background: none; font-size: inherit;
              text-decoration: underline; text-decoration-color: var(--border-2); }

.bars { list-style: none; margin: var(--s2) 0 0; padding: 0; }
.barlink {
  display: grid; grid-template-columns: minmax(72px, 1.1fr) minmax(60px, 2fr) auto;
  gap: var(--s2); align-items: center; min-height: var(--tap);
  text-decoration: none; color: var(--text); font-size: .8125rem;
}
.barlink:hover { background: var(--surface-2); }
.barname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar { width: 100%; height: 8px; }
.bar .fill { fill: var(--ink); }
.bar .cap { fill: var(--down); }
.barn { color: var(--muted); font-variant-numeric: tabular-nums; text-align: right; }
.openb { color: var(--down); font-size: .6875rem; }

/* Coverage: NO all-zero bar chart. An all-zero chart is a row of empty tracks indistinguishable
   from a broken collector. Here the MISSING segment is the only visible ink. */
.rails { list-style: none; margin: var(--s2) 0 0; padding: 0; }
.rails li { display: grid; grid-template-columns: minmax(80px,1fr) minmax(60px,1.4fr) auto;
            gap: var(--s2); align-items: center; min-height: 32px; font-size: .8125rem; }
.raill { color: var(--muted); }
.rail-svg { width: 100%; height: 8px; }
.rail-svg .have { fill: var(--surface-3); }
.rail-svg .miss { fill: url(#p-unknown); }
.railn { color: var(--text); font-variant-numeric: tabular-nums; }

.values { margin-top: var(--s3); border-top: 1px solid var(--border); padding-top: var(--s2); }
.values > summary { min-height: 32px; display: flex; align-items: center;
                    font-size: .75rem; color: var(--muted); cursor: pointer; }
.values table { width: 100%; border-collapse: collapse; font-size: .75rem; margin-top: var(--s2); }
.values th, .values td { text-align: left; padding: 4px 6px; border-bottom: 1px solid var(--grid); }
.values th { color: var(--muted); font-weight: 600; }
.values td.num { text-align: right; font-variant-numeric: tabular-nums; }
.values td.mono { font-family: var(--mono); }
.valwrap { overflow-x: auto; }

/* ═══ 5 · the list ════════════════════════════════════════════════════════════════════════ */
.listwrap { margin-bottom: var(--s6); }
.listbar {
  position: sticky; top: 60px; z-index: 20;
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2);
  padding: var(--s2) 0; margin-bottom: var(--s2);
  background: var(--bg); border-bottom: 1px solid var(--border);
}
.listbar .lbl { margin: 0; }
.sel { display: inline-flex; }
.sel select {
  min-height: var(--tap); padding: 0 var(--s2);
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r);
  font: inherit; font-size: .8125rem;
}
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip-f {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px; padding: 0 var(--s2) 0 var(--s3);
  background: var(--surface-3); border: 1px solid var(--border-2); border-radius: var(--r-pill);
  font: 600 .75rem var(--mono); color: var(--text);
}
.chip-f button { width: 32px; height: 32px; margin-right: -6px; background: none; border: 0;
                 color: var(--muted); cursor: pointer; font-size: 15px; line-height: 1; }
.chip-f button:hover { color: var(--text); }

/* A search box that can conceal an outage is a regression, not a feature. */
.receipt {
  margin: 0 0 var(--s2); padding: var(--s2) var(--s3);
  border: 1px solid var(--crit); border-left: 4px solid var(--crit); border-radius: var(--r);
  background: var(--surface); color: var(--text); font-size: .8125rem;
}
.receipt b { color: var(--crit); }
.receipt button { margin-left: var(--s2); min-height: 32px; padding: 0 var(--s2);
                  background: var(--surface-3); border: 1px solid var(--border-2);
                  border-radius: var(--r-sm); color: var(--text); font: inherit;
                  font-size: .75rem; cursor: pointer; }
.empty {
  margin: var(--s4) 0; padding: var(--s5) var(--s4); text-align: center;
  border: 1px dashed var(--border-2); border-radius: var(--r-lg); color: var(--muted);
  font-size: .9375rem;
}
.empty b { color: var(--text); }
.empty button { display: inline-block; margin-top: var(--s3); min-height: var(--tap);
                padding: 0 var(--s4); background: var(--surface-2);
                border: 1px solid var(--border); border-radius: var(--r);
                color: var(--text); font: inherit; font-size: .875rem; cursor: pointer; }

/* ── column headings ─────────────────────────────────────────────────────────────────────────
   Six unlabelled numeric columns are unreadable. This shares the ROW's grid definition verbatim,
   so the header cannot drift out of alignment with the data beneath it. Below 1000px the row
   stacks into three areas and no single header line could align, so it is hidden there and a
   legend plus per-cell title tooltips carry the meaning instead. */
.rowhead { display: none; }
.rh {
  font-size: .625rem; font-weight: 650; letter-spacing: .07em; text-transform: uppercase;
  color: var(--dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rh-rid { grid-area: rid; }  .rh-addr { grid-area: addr; }
.rh-spark { grid-area: spark; text-align: center; }
.rh-lat { grid-area: lat; text-align: right; }
.rh-code { grid-area: code; text-align: right; }
.rh-cert { grid-area: cert; text-align: right; }
.rh-disk { grid-area: disk; text-align: right; }
.rh-drift { grid-area: drift; text-align: right; }
.rh-apt { grid-area: apt; text-align: right; }
.rh-for { grid-area: for; text-align: right; }

/* the phone substitute: names the only two numeric columns that survive there */
.legend {
  margin: 0 0 var(--s2); padding: 0 var(--s1);
  font-size: .6875rem; color: var(--dim);
}
@media (min-width: 1000px) { .legend { display: none; } }


.rows { display: flex; flex-direction: column; }
.ghead {
  margin: var(--s4) 0 var(--s1); padding-bottom: var(--s1);
  font-size: .6875rem; font-weight: 650; text-transform: uppercase; letter-spacing: .09em;
  color: var(--dim); border-bottom: 1px solid var(--grid);
}
.ghead:first-child { margin-top: 0; }

/* ── the row ───────────────────────────────────────────────────────────────────────────────
   Six fields on a phone, ten at >=1000px, and a reason line that only exists when something is
   wrong. A healthy fleet renders as 34 identical two-line rows; a broken one grows exactly where
   it hurts. Density is the resting state; detail is the exception. */
.row {
  position: relative;
  display: grid;
  grid-template-columns: 3px 18px minmax(0,1fr) auto auto;
  grid-template-areas:
    "rail glyph rid   .     lat"
    "rail glyph addr  spark cert"
    "rail why   why   why   why";
  column-gap: var(--s2); align-items: center;
  min-height: 56px; padding: var(--s2) var(--s2) var(--s2) 0;
  border-bottom: 1px solid var(--grid);
  text-decoration: none; color: var(--text);
}
.row:hover { background: var(--surface-2); }
.row:focus-visible { outline-offset: -2px; }

.rail { grid-area: rail; align-self: stretch; border-radius: 2px; background: var(--ok); }
.glyph { grid-area: glyph; font: 700 12px/1 var(--mono); color: var(--ok); text-align: center; }
.rid   { grid-area: rid; min-width: 0; display: flex; align-items: baseline; gap: 6px;
         white-space: nowrap; overflow: hidden; }
.rname { font-size: .9375rem; font-weight: 600; letter-spacing: -.005em;
         overflow: hidden; text-overflow: ellipsis; }

/* Homograph guard 1: the last octet ALWAYS rides beside the name, never truncated, colour-banded
   by /24 in ACHROMATIC greys so an address band can never be misread as a status. */
.oct { font: 600 .75rem/1 var(--mono); flex: none; }
.oct-a { color: var(--net-a); } .oct-b { color: var(--net-b); }
.oct-c { color: var(--net-c); } .oct-d { color: var(--net-d); }

/* Homograph guard 2: a TRIVALENT Odoo chip. "runs nothing" and "never measured" are different
   facts and are drawn differently. Famousa [18] can never be read as Famousa19 [19]. */
.vchip {
  flex: none; padding: 1px 6px; border-radius: var(--r-sm);
  background: var(--surface-3); border: 1px solid var(--border);
  font: 650 .6875rem/1.5 var(--mono); color: var(--text);
}
.vchip-no { background: none; color: var(--dim); border-style: solid; border-color: var(--border-2); }
.vchip-q  { background: none; color: var(--unknown); border: 1px dashed var(--unknown); }

/* Homograph guard 3: the ADDRESS is the fact, not the decoration. It is full-contrast mono at
   every density; in the old board it was --muted, which made the name the only thing you read. */
.raddr {
  grid-area: addr; min-width: 0;
  font: .75rem/1.4 var(--mono); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rspark { grid-area: spark; width: 72px; }
.spark { display: block; width: 72px; height: 16px; }
.sb-ok   { fill: var(--ink); }
.sb-slow { fill: var(--warn); }
/* A dropout plotted as 0 ms reads as "extremely fast". It is drawn as a full-height gap bar. */
.sb-gap  { fill: url(#p-gap); }

.m { font: .8125rem/1 var(--mono); color: var(--text); white-space: nowrap; text-align: right; }
.m i { font-style: normal; color: var(--dim); font-size: .6875rem; margin-left: 1px; }
.m-lat  { grid-area: lat; }
.m-cert { grid-area: cert; }
.m-code, .m-disk, .m-drift, .m-apt, .m-for { display: none; }

/* A value we could not measure is never blank, never zero and never green — it is LOUDER than any
   healthy number on the row. */
.na { color: var(--unknown); font-weight: 650; }

/* The 24-hour tier: the value and its age are ONE token under one underline. No breakpoint and no
   future edit can print "1162 behind" without "6h" beside it. */
.t24 {
  display: inline-flex; align-items: baseline; gap: 4px;
  border-bottom: 1px dotted var(--border-2); padding-bottom: 1px;
}
.t24v { color: var(--text); font-variant-numeric: tabular-nums; }
.t24a { color: var(--dim); font-size: .6875rem; }
.t24-old { border-bottom-color: var(--stale); }
.t24-old .t24v, .t24-old .t24a { color: var(--stale); }
.t24-inline { border-bottom: 0; }
.agechip { font: .6875rem var(--mono); }
.agechip .t24v { color: var(--dim); }

.rwhy {
  grid-area: why; display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2);
  margin-top: var(--s1); font-size: .8125rem; color: var(--text);
}
.why1 { font-weight: 550; }
.whyn { color: var(--muted); font-size: .75rem; }
/* A DOWN alert you cannot act on is the actual 2am failure mode. */
.nopath { color: var(--warn); font-size: .6875rem; border: 1px dashed var(--warn);
          border-radius: var(--r-sm); padding: 1px 6px; }

/* status: rule weight + rule style + glyph + surface + (for absent data) a hatch */
.s-ok      .rail { background: var(--ok); }
.s-warn    .rail { background: var(--warn); box-shadow: inset 0 0 0 1px var(--warn); }
.s-crit    .rail, .s-down .rail { background: var(--crit); }
.s-unknown .rail { background: repeating-linear-gradient(180deg, var(--unknown) 0 4px, transparent 4px 8px); }
.s-stale   .rail { background: repeating-linear-gradient(180deg, var(--stale) 0 4px, transparent 4px 8px); }
.s-maintenance .rail { background: repeating-linear-gradient(180deg, var(--maint) 0 2px, transparent 2px 6px); }
.s-ok .glyph { color: var(--ok); }
.s-warn .glyph { color: var(--warn); }
.s-crit .glyph { color: var(--crit); }
.s-down .glyph { color: var(--down); }
.s-unknown .glyph { color: var(--unknown); }
.s-stale .glyph { color: var(--stale); }
.s-maintenance .glyph { color: var(--maint); }

.row:not(.s-ok) { background: var(--surface); }
.row.row-big { border-radius: var(--r); border: 1px solid var(--border); border-bottom-width: 1px;
               margin-bottom: var(--s2); padding-left: 0; }
.row.row-big .rail { border-radius: var(--r) 0 0 var(--r); }
/* "We have no measurement here" must read as a texture, never as calm empty space. */
.s-unknown, .s-stale {
  background-image: repeating-linear-gradient(135deg, var(--tint-row) 0 6px, transparent 6px 13px);
}

.row .hl { background: var(--surface-3); border-radius: 3px; box-shadow: 0 0 0 1px var(--border-2); }
.row mark { background: var(--surface-3); color: var(--text); border-radius: 3px;
            box-shadow: 0 0 0 1px var(--border-2); }

/* compact: drop the address line and the sparkline, keep every number */
.compact .row {
  min-height: 40px;
  grid-template-areas:
    "rail glyph rid  cert lat"
    "rail why   why  why  why";
}
.compact .raddr { display: none; }
.compact .rspark { display: none; }
@media (pointer: coarse) { .compact .row { min-height: var(--tap); } }

/* desktop: ten fields, one line, and the address gets its own column as a third discriminator */
@media (min-width: 1000px) {
  .row {
    grid-template-columns: 3px 18px minmax(132px, 1fr) minmax(0, 1.4fr) 72px 56px 42px 50px 62px 74px 58px 44px;
    grid-template-areas:
      "rail glyph rid addr spark lat code cert disk drift apt for"
      "rail why   why why  why   why why  why  why why   why why";
    min-height: 40px;
  }
  .raddr { font-size: .8125rem; }
  .m-code, .m-disk, .m-drift, .m-apt, .m-for { display: block; }
  .m-code { grid-area: code; color: var(--muted); }
  .m-drift { grid-area: drift; }
  .m-disk { grid-area: disk; }
  .m-apt { grid-area: apt; }
  .m-for { grid-area: for; color: var(--muted); }
  .rowhead {
    display: grid;
    grid-template-columns: 3px 18px minmax(132px, 1fr) minmax(0, 1.4fr) 72px 56px 42px 50px 62px 74px 58px 44px;
    grid-template-areas: "rail glyph rid addr spark lat code cert disk drift apt for";
    column-gap: var(--s2); align-items: end;
    padding: var(--s2) var(--s2) 6px 0;
    border-bottom: 1px solid var(--border);
    background: var(--bg);
  }
  /* only the list header sticks — the attention header scrolls away with its two rows */
  .rowhead-list { position: sticky; top: 106px; z-index: 3; }
  .rows-now + .rows-now { margin-top: 0; }
  .listbar { top: 61px; }
  .verdict { padding-top: var(--s6); }
  .vline { font-size: 1.75rem; }
}

.foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
  padding: var(--s4) 0; margin-top: var(--s5);
  border-top: 1px solid var(--border); color: var(--muted); font-size: .8125rem;
}
.foot b { color: var(--text); }

/* ═══ help sheet ══════════════════════════════════════════════════════════════════════════ */
.sheet {
  width: min(760px, calc(100vw - 32px)); max-height: 86dvh;
  padding: var(--s5); border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); color: var(--text); box-shadow: var(--shadow);
}
.sheet::backdrop { background: rgba(1, 4, 9, .72); }
.sheet h2 { margin: 0 0 var(--s4); font-size: 1.125rem; }
.sheet h3 { margin: 0 0 var(--s2); font-size: .75rem; text-transform: uppercase;
            letter-spacing: .09em; color: var(--muted); }
.sheetgrid { display: grid; gap: var(--s5); }
@media (min-width: 720px) { .sheetgrid { grid-template-columns: 1fr 1fr; } }
.kv { margin: 0 0 var(--s3); display: grid; grid-template-columns: auto 1fr; gap: 4px var(--s3);
      font-size: .8125rem; }
.kv dt { font-family: var(--mono); color: var(--text); font-weight: 600; white-space: nowrap; }
.kv dd { margin: 0; color: var(--muted); }
.sheet code { font-family: var(--mono); background: var(--bg); padding: 1px 5px;
              border-radius: 4px; font-size: .9em; }

/* ═══ motion — two animations in the whole page ═══════════════════════════════════════════ */
@keyframes breathe { 0%,100% { opacity: 1; } 50% { opacity: .42; } }
/* A CRIT arriving live: one 900ms edge settle, then it stops. What PERSISTS is static — a NEW pip
   that outlives the animation, so the information survives a glance away and reduced motion. */
/* The sweep rides on box-shadow, NEVER on background. Animating the background of a status mark
   overrides its status colour for the duration — a newly-critical ribbon mark briefly went
   INVISIBLE, i.e. the alarm hid the thing it was alarming about. Caught in the browser. */
@keyframes arrive    { from { box-shadow: inset 0 0 0 2px var(--down); }
                       to   { box-shadow: inset 0 0 0 2px transparent; } }
@keyframes arrive-mk { from { box-shadow: 0 0 0 3px var(--down); }
                       to   { box-shadow: 0 0 0 3px transparent; } }
.row.arrive { animation: arrive .9s cubic-bezier(.2,.8,.3,1); }
.mk.arrive .mkb { animation: arrive-mk .9s cubic-bezier(.2,.8,.3,1); }
.row.isnew::after, .mk.isnew::after {
  content: "NEW"; position: absolute; right: 2px; top: 2px;
  font: 700 8px/1.4 var(--mono); letter-spacing: .06em;
  color: var(--bg); background: var(--down); padding: 1px 4px; border-radius: 3px;
}
.mk.isnew::after { content: ""; padding: 0; width: 5px; height: 5px; border-radius: var(--r-pill); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   LOGIN
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
.login-wrap { min-height: 100dvh; display: grid; place-items: center; padding: var(--s4); }
.login {
  width: 100%; max-width: 380px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--s5); box-shadow: var(--shadow);
}
.login h1 { margin: 0 0 var(--s1); font-size: 1.25rem; letter-spacing: -.02em; }
.login .sub { margin: 0 0 var(--s5); color: var(--muted); font-size: .875rem; }
.field { margin-bottom: var(--s4); }
.field label { display: block; margin-bottom: 6px; font-size: .875rem; font-weight: 550; }
.field input {
  width: 100%; min-height: var(--tap); padding: 0 var(--s3);
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r);
  font: inherit; font-size: 1rem;      /* 16px stops iOS zooming the page on focus */
}
.field input:focus { border-color: var(--border-2); }
.btn-primary {
  width: 100%; min-height: var(--tap);
  background: var(--text); color: var(--bg);
  border: none; border-radius: var(--r);
  font: inherit; font-size: 1rem; font-weight: 650; cursor: pointer;
}
.btn-primary:hover { opacity: .9; }
.alert {
  margin-bottom: var(--s4); padding: var(--s3);
  border: 1px solid var(--crit); border-left: 4px solid var(--crit); border-radius: var(--r);
  color: var(--crit); font-size: .875rem;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   HOST DETAIL  +  AUDIT  (kept intact; the restart gauntlet is untouched)
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
header {
  position: sticky; top: 0; z-index: 10;
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s4);
  padding-top: max(var(--s2), env(safe-area-inset-top));
  background: var(--surface); border-bottom: 1px solid var(--border);
}
header h1 { font-size: 1rem; margin: 0; font-weight: 600; letter-spacing: -.01em;
            overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
header .spacer { flex: 1; }
.btn {
  min-height: var(--tap); min-width: var(--tap);
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s2); padding: 0 var(--s4);
  background: var(--surface-2); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r);
  font: inherit; font-size: .875rem; cursor: pointer; text-decoration: none;
}
.btn:hover { background: var(--surface-3); border-color: var(--border-2); }

.detail, .panel { padding: var(--s4); border-bottom: 1px solid var(--border);
                  max-width: 1000px; margin: 0 auto; }
.panel h2 {
  font-size: .75rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--muted); margin: 0 0 var(--s3); font-weight: 650;
  display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
}
.panel h3 { font-size: 1rem; margin: 0 0 var(--s1); }
.age { font-weight: 400; text-transform: none; letter-spacing: 0; font-size: .75rem; color: var(--dim); }

.host-top { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap; }
.host-name { font-weight: 600; font-size: .9375rem; }
.host-domain { font-size: .8125rem; color: var(--text); font-family: var(--mono); word-break: break-all; }

/* status chip: glyph + label, so colour is never the only channel */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 2px var(--s2); border-radius: var(--r-pill);
  font-size: .75rem; font-weight: 650; letter-spacing: .03em; text-transform: uppercase;
  border: 1px solid currentColor;
}
.chip::before { font-family: var(--mono); font-weight: 700; }
/* Scoped to .chip deliberately. An unscoped .c-down::before leaks its glyph onto every other
   element carrying the status class — the fleet count buttons rendered "✕ ✕ 2 DOWN". */
.chip.c-down::before    { content: "\2715"; }
.chip.c-crit::before    { content: "\26A0"; }
.chip.c-warn::before    { content: "\25B2"; }
.chip.c-unknown::before { content: "?"; }
.chip.c-stale::before   { content: "\21BB"; }
.chip.c-maintenance::before { content: "\2699"; }
.chip.c-ok::before      { content: "\2713"; }

.idblock { display: flex; flex-direction: column; gap: var(--s1); font-size: .875rem;
           margin-top: var(--s3); }
.idblock .k { display: inline-block; min-width: 92px; color: var(--muted); }
.idblock .v { color: var(--text); word-break: break-all; }

.factgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: var(--s2); }
.fact {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r); padding: var(--s2) var(--s3);
  display: flex; flex-direction: column;
}
.fact .n { font-size: 1.375rem; font-weight: 650; font-variant-numeric: tabular-nums; line-height: 1.2; }
.fact .l { font-size: .6875rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }

.meta { margin-top: var(--s2); display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3);
        font-size: .8125rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.meta b { color: var(--text); font-weight: 600; }
.upd { display: flex; gap: var(--s3); flex-wrap: wrap; font-size: .8125rem; margin-top: var(--s2);
       padding-top: var(--s2); border-top: 1px dashed var(--border); color: var(--muted); }
.upd b { color: var(--text); font-variant-numeric: tabular-nums; }
.upd .stale { color: var(--dim); }

.reasons { margin: var(--s2) 0 0; padding-left: 1.1em; font-size: .8125rem; color: var(--muted); }
.reasons li { margin: 2px 0; }
.reasons li.worst { color: var(--text); font-weight: 550; }

.norecovery {
  margin-top: var(--s2); padding: var(--s2) var(--s3);
  border: 1px dashed var(--warn); border-radius: var(--r-sm);
  font-size: .8125rem; color: var(--warn);
}

.btn-action {
  min-height: 48px; width: 100%;
  background: var(--surface-2); color: var(--text);
  border: 1.5px solid var(--border); border-radius: var(--r);
  font: inherit; font-size: .9375rem; font-weight: 600; cursor: pointer;
}
.btn-action:hover { border-color: var(--border-2); }
.btn-action:disabled { opacity: .5; cursor: default; }

#sheet {
  margin-top: var(--s4); padding: var(--s4);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r);
}
#sheet label { display: block; margin: var(--s3) 0 6px; font-size: .875rem; font-weight: 550; }
#sheet input[type=text] {
  width: 100%; min-height: var(--tap); padding: 0 var(--s3);
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r);
  font: inherit; font-size: 1rem;
}
#sheet code { background: var(--bg); padding: 1px 5px; border-radius: 4px; font-family: var(--mono); }

.checks { list-style: none; padding: 0; margin: 0; font-size: .8125rem; }
.checks li { padding: 3px 0; }
.checks .lv-ok { color: var(--muted); }
.checks .lv-warn { color: var(--warn); }
.checks .lv-block { color: var(--crit); font-weight: 600; }
.preflight { font-size: .875rem; color: var(--muted); }

.btn-danger {
  position: relative; overflow: hidden;
  min-height: 52px; width: 100%; margin-top: var(--s4);
  background: #b3121f; color: #fff;
  border: none; border-top: 2px solid #ff1e3c; border-radius: var(--r);
  font: inherit; font-size: 1rem; font-weight: 700; letter-spacing: .04em;
  cursor: pointer; touch-action: none; user-select: none;
}
.btn-danger:disabled { opacity: .55; cursor: default; }
.btn-danger::after { content: ""; position: absolute; inset: 0 100% 0 0;
                     background: rgba(255,255,255,.28); transition: none; }
.btn-danger.holding::after { inset: 0 0 0 0; transition: inset 1.2s linear; }
@media (prefers-reduced-motion: reduce) { .btn-danger.holding::after { transition: none; } }

#result { margin-top: var(--s3); font-size: .875rem; }
#result p { margin: var(--s2) 0; }

.auditlist { list-style: none; padding: 0; margin: 0; font-size: .8125rem; }
.auditlist li { padding: 5px 0; border-bottom: 1px solid var(--grid); display: flex;
                gap: var(--s2); flex-wrap: wrap; align-items: baseline; }
.chip-sm { font-size: .6875rem; padding: 1px 7px; border-radius: var(--r-pill);
           border: 1px solid currentColor; text-transform: uppercase; font-weight: 650; }
.o-ok, .o-allowed { color: var(--ok); }
.o-denied, .o-failed { color: var(--crit); }
.o-dispatching { color: var(--muted); }
.o-ACTION-UNKNOWN { color: var(--warn); }
.detailbox { background: var(--bg); border: 1px solid var(--border); border-radius: var(--r-sm);
             padding: var(--s2); font-size: .75rem; overflow-x: auto; margin: var(--s2) 0 0;
             white-space: pre-wrap; word-break: break-word; font-family: var(--mono); }
.cardlink { color: inherit; text-decoration: none; display: block; }
.cardlink:hover .host-name { text-decoration: underline; }

/* the audit page reuses .hosts/.host as a card list */
.hosts { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s4);
         max-width: 1000px; margin: 0 auto; }
.host {
  background: var(--surface); border: 1px solid var(--border);
  border-left: 4px solid var(--border); border-radius: var(--r); padding: var(--s3);
}
.host.s-down, .host.a-failed { border-left-color: var(--crit); border-left-width: 6px; }
.host.s-crit { border-left-color: var(--crit); border-left-width: 6px; }
.host.s-warn, .host.a-ACTION-UNKNOWN { border-left-color: var(--warn); }
.host.s-unknown, .host.a-denied { border-left-color: var(--unknown); border-left-style: dashed; }
.host.s-stale { border-left-color: var(--stale); border-left-style: dashed; }
.host.s-maintenance { border-left-color: var(--maint); border-left-style: dotted; }
.host.s-ok, .host.a-ok, .host.a-allowed { border-left-color: var(--ok); }

/* ── storage rails ───────────────────────────────────────────────────────────────────────────
   Bar width is an SVG presentation attribute computed in stats.py, not a CSS width: `style-src
   'self'` blocks the inline style="width:62%" this would normally be written with. */
.rails-disk li { display: grid; grid-template-columns: minmax(84px,1fr) minmax(60px,2fr) auto;
                 align-items: center; gap: var(--s2); padding: 3px 0; }
.rails-disk .raill { font-size: .8125rem; overflow: hidden; text-overflow: ellipsis;
                     white-space: nowrap; }
.rails-disk .rail-svg { width: 100%; height: 8px; }
.rails-disk .track { fill: var(--grid); }
.rails-disk .used  { fill: var(--ink); }
.rails-disk .u-warn { fill: var(--warn); }
.rails-disk .u-crit { fill: var(--crit); }
.rails-disk .railn { font-size: .75rem; color: var(--muted); white-space: nowrap;
                     font-variant-numeric: tabular-nums; }
.rails-disk .railn b { color: var(--text); }
.rails-disk .railn i { font-style: normal; color: var(--dim); margin-left: 2px; }
.railpct { margin-left: var(--s2); color: var(--dim); }
.raildays { margin-left: var(--s2); color: var(--warn); }

/* ═══ Fleet SOS: /pair ════════════════════════════════════════════════════════════════════════
   The QR is an inline <svg> with presentation-attribute colours (black on white in BOTH themes —
   a scanner needs contrast, not a theme). Size comes from this class, never from style="". */
.paircode { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s3); }
.pcode {
  margin: 0; display: flex; gap: .5em; flex-wrap: wrap;
  font-size: clamp(2rem, 9vw, 3.25rem); font-weight: 700; letter-spacing: .12em;
  font-variant-numeric: tabular-nums; line-height: 1.1; color: var(--text);
}
.pcode span { padding: var(--s1) var(--s2); background: var(--surface-2);
              border: 1px solid var(--border); border-radius: var(--r); }
.qrbox { background: #ffffff; padding: var(--s3); border-radius: var(--r);
         border: 1px solid var(--border); line-height: 0; }
.qr { display: block; width: min(280px, calc(100vw - 88px)); height: auto; }
.pairlink { word-break: break-all; }
.pairform { margin-top: var(--s4); }
.devlist { list-style: none; margin: 0; padding: 0; }
.devlist li { display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
              padding: var(--s3) 0; border-bottom: 1px solid var(--grid); flex-wrap: wrap; }
.devlist li:last-child { border-bottom: 0; }
.devmain { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s2);
           min-width: 0; }
.devmain .meta { flex-basis: 100%; margin-top: 0; }
.btn-revoke { color: var(--crit); border-color: var(--crit); }
.btn-revoke:hover { background: var(--tint-crit); border-color: var(--crit); }

/* dashboard header: the "Phones" link, achromatic like the rest of the chrome */
.navlink {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: var(--tap); padding: 0 var(--s2); border-radius: var(--r);
  color: var(--muted); text-decoration: none; font-size: .8125rem; font-weight: 600;
  border: 1px solid transparent;
}
.navlink:hover { color: var(--text); background: var(--surface); border-color: var(--border); }
@media (max-width: 599px) { .navlink .navlink-t { display: none; } }
