/* Nous Uptime UI.
   Type scale follows docs/brand-guidelines.md "Digital Type Scale"; colours follow
   its palette and lib/reports-publish.cjs, so this and the published snapshot read
   as one system.

   THE ACCENT SPLITS INTO TWO ROLES, and that is why a light theme is more than an
   inverted palette. The Nous green is 1.26:1 on white — unusable as text. The brand
   guidelines say so ("green accent on white = lower contrast") and the report
   chrome's print flip proves the intent, replacing accent text with #111. So:

     --accent      a FILL. Green in both modes, always paired with --accent-on.
     --accent-on   text sitting on that fill. Near-black in both modes.
     --link        emphasis/link TEXT. Green on dark, near-black on light.

   Green is NOT used for LINKS in data tables — 56 lime domains beside 56 mint "up"
   chips is two yellow-greens competing, which collapses for protan and deutan
   viewers and in low light. Table links are --ink; the row hover is the affordance.
   Because links gave the lime up, mint green is free to do its real job in the state
   column, where colour is the fast identifier for scanning 56 rows to find the wrong
   one. Lime is reserved for the active nav and "needs a human decision".

   Every colour value was measured, not guessed. Ratios are against that mode's --bg.
*/

:root {
  color-scheme: dark;

  --bg: #0A0A0A;
  --bg-card: #131313;
  --bg-hover: #1F1F1F;
  --ink: #EDEDED;          /* 16.91:1. Not #FFFFFF: pure white on near-black blooms
                              (halation), which makes small text harder to read and
                              was most of why dark mode felt worse than light. */
  --ink-dim: #A8A8A8;      /*  8.33:1 */
  --ink-dimmer: #808080;   /*  5.01:1 */
  --line: #262626;
  --line-hi: #3A3A3A;
  --line-input: #616161;   /*  3.20:1 — control borders need WCAG 1.4.11 non-text
                              contrast of 3:1 to be identifiable */
  --accent: #D7F23B;
  --accent-on: #0A0A0A;
  --link: #D7F23B;         /* 15.70:1 */
  --ok: #8CF796;           /* 14.95:1 */
  --bad: #FF6B6B;          /*  7.13:1 */
  --bad-on: #0A0A0A;       /*  7.63:1 on --bad */
  --warn: #FFC857;         /* 12.87:1 */
  --spark: #A8A8A8;        /* not lime: the chart should not compete with the accent,
                              leaving red as the only colour inside the plot */
}

/* Light. Applied on explicit choice, or when the OS prefers it and no choice was
   made. data-theme is set before first paint by an inline script in <head>, so
   there is no flash of the wrong theme. */
:root[data-theme="light"] {
  color-scheme: light;

  --bg: #FFFFFF;
  --bg-card: #F7F7F7;
  --bg-hover: #EFEFEF;
  --ink: #111111;          /* 18.88:1 */
  --ink-dim: #555555;      /*  7.46:1 */
  --ink-dimmer: #5F5F5F;   /*  6.39:1 */
  --line: #E4E4E4;
  --line-hi: #D8D8D8;
  --line-input: #8E8E8E;   /*  3.28:1 */
  --accent: #D7F23B;
  --accent-on: #0A0A0A;
  --link: #111111;         /* 18.88:1 — the brand's light treatment uses black, not
                              green, for text */
  --ok: #15703A;           /*  6.16:1 — #8CF796 is 1.32:1 on white */
  --bad: #B3261E;          /*  6.54:1 — #FF6B6B is 2.78:1 */
  --bad-on: #FFFFFF;       /*  6.54:1 on --bad */
  --warn: #7A4F00;         /*  7.13:1 — #FFC857 is 1.54:1 */
  --spark: #555555;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;
    --bg: #FFFFFF; --bg-card: #F7F7F7; --bg-hover: #EFEFEF;
    --ink: #111111; --ink-dim: #555555; --ink-dimmer: #5F5F5F;
    --line: #E4E4E4; --line-hi: #D8D8D8; --line-input: #8E8E8E;
    --accent: #D7F23B; --accent-on: #0A0A0A; --link: #111111;
    --ok: #15703A; --bad: #B3261E; --bad-on: #FFFFFF; --warn: #7A4F00; --spark: #555555;
  }
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* 100%, not a px value. A fixed px base silently overrides the reader's own browser
   text-size setting, so anyone who has turned it up gets nothing. Everything below
   is rem, so browser zoom and OS text scaling both work. */
html { font-size: 100%; }

body {
  font-family: 'Hanken Grotesk', system-ui, -apple-system, sans-serif;
  background: var(--bg);
  color: var(--ink);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

.wrap { max-width: 1400px; margin: 0 auto; padding: 0 24px; }
@media (max-width: 768px) { .wrap { padding: 0 14px; } }

a { color: var(--link); text-decoration: none; }
/* Prose links are underlined in BOTH modes. In dark mode the lime and the body grey
   are within ~1.05:1 of each other in luminance, so hue would be the only signal,
   which fails WCAG 1.4.1. */
p a, dd a, li a, .lede a { text-decoration: underline; }
a:hover { text-decoration: underline; }

/* Keyboard operation was previously invisible everywhere except text inputs. */
:where(a, button, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
  border-radius: 2px;
}

.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;
}

/* ── Type scale (docs/brand-guidelines.md, Digital Type Scale) ────────────── */

h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -0.015em; margin: 1.25rem 0 0.25rem; }
h2 { font-size: 1.25rem; font-weight: 600; margin: 1.6rem 0 0.5rem; }
h3 { font-size: 1rem; font-weight: 600; margin: 1.1rem 0 0.35rem; }

/* Section headings and card titles are different KINDS of type, not different
   sizes. Previously both were bold h2 at similar sizes, so they read as one
   undifferentiated object. */
.sect, .tablehead {
  margin-top: 2.75rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
.sect > h2, .tablehead h2 { font-size: 1.25rem; font-weight: 600; margin: 0 0 0.6rem; }

.card > h2:first-child,
details.card summary h2 {
  font-size: 0.8125rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--ink-dim); margin: 0 0 0.75rem;
}
.card h3 { font-size: 1rem; font-weight: 600; margin: 1.5rem 0 0.35rem; }

p { margin: 0.4rem 0; }
.lede { color: var(--ink-dim); max-width: 90ch; margin-bottom: 1rem; }
.muted { color: var(--ink-dim); }
.small { font-size: 0.875rem; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.grow { flex: 1; }
.mt { margin-top: 0.6rem; }
.ok { color: var(--ok); }
.bad { color: var(--bad); }
.amber { color: var(--warn); }

/* No font-size here, deliberately. `.mono` and `.small` are both single-class
   selectors, so on `class="small mono"` source order decided the winner and the old
   `font-size: 0.92em` silently made mono cells SMALLER than the prose beside them.
   Size is set by the table, not by the family. */
.mono {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
}
code, pre {
  font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
  font-size: 0.9375rem;
}
code { background: var(--bg-card); padding: 0.1em 0.35em; border: 1px solid var(--line); border-radius: 3px; }
pre { white-space: pre-wrap; line-height: 1.6; }

/* ── Top bar ─────────────────────────────────────────────────────────────── */

.topbar { border-bottom: 1px solid var(--line); background: var(--bg); position: sticky; top: 0; z-index: 50; }
.topbar .wrap { display: flex; align-items: center; gap: 20px; height: 56px; }
.brand { font-weight: 800; letter-spacing: 0.1em; font-size: 1rem; color: var(--ink); text-transform: uppercase; }
.brand span { color: var(--link); }
.topbar nav { display: flex; gap: 2px; margin-left: 10px; flex-wrap: wrap; }
.topbar nav a { color: var(--ink-dim); padding: 6px 11px; border-radius: 4px; font-weight: 600; font-size: 0.9375rem; }
.topbar nav a:hover { background: var(--bg-hover); color: var(--ink); text-decoration: none; }
.topbar nav a.on { color: var(--accent-on); background: var(--accent); }
.who { margin-left: auto; display: flex; align-items: center; gap: 12px; font-size: 0.875rem; }

/* ── Alerts and callouts ─────────────────────────────────────────────────── */

.alert { padding: 12px 16px; border: 1px solid var(--line); border-left-width: 3px; margin: 14px 0; background: var(--bg-card); border-radius: 3px; }
.alert-ok   { border-left-color: var(--ok); }
.alert-warn { border-left-color: var(--warn); }
.alert-bad  { border-left-color: var(--bad); }
.alert-note { border-left-color: var(--line-hi); color: var(--ink-dim); }

/* A stale-probe warning that scrolls away is useless: on a 2,400px table you lose it
   immediately and have no idea the page is a photograph. */
/* Sticky so it cannot scroll away on a 2,400px table, but still rounded — a
   full-bleed square bar read as a browser chrome warning rather than part of the app. */
.alert-stale {
  position: sticky; top: 64px; z-index: 40; margin: 14px 0; border-radius: 4px;
  border: 1px solid var(--bad); border-left-width: 4px;
  background: color-mix(in srgb, var(--bad) 14%, var(--bg-card));
  box-shadow: 0 4px 16px rgb(0 0 0 / 0.35);
}

.hint { color: var(--ink-dim); font-size: 0.9375rem; border-left: 2px solid var(--line-hi); padding-left: 12px; margin: 0.8rem 0; max-width: 100ch; }

.callout { border: 1px solid var(--line); border-radius: 4px; padding: 16px 18px; margin: 18px 0; background: var(--bg-card); }
.callout h2 { font-size: 1.25rem; font-weight: 700; margin: 0 0 0.4rem; }
.callout p { color: var(--ink-dim); font-size: 0.9375rem; max-width: 100ch; }
.callout-new  { border-left: 3px solid var(--accent); }
.callout-warn { border-left: 3px solid var(--warn); }
/* An outage callout must not be one 1.3:1 step off the page background. */
.callout-bad {
  border-color: var(--bad); border-left: 4px solid var(--bad);
  background: color-mix(in srgb, var(--bad) 10%, var(--bg-card));
}

/* ── Stat row ────────────────────────────────────────────────────────────── */

.statrow { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); margin: 18px 0; }
.stat { background: var(--bg); padding: 14px 16px; display: flex; flex-direction: column; gap: 3px; }
/* Hanken 800 upright, per the brand doc's digital scale. Lora italic with
   proportional figures made the down count jitter between reloads and read like a
   pull quote rather than an instrument. */
.stat .n { font-size: 2rem; font-weight: 800; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.stat .l { color: var(--ink-dim); font-size: 0.875rem; }
.stat.bad  .n { color: var(--bad); }
.stat.warn .n { color: var(--warn); }

/* ── Tables ──────────────────────────────────────────────────────────────── */

table { width: 100%; border-collapse: collapse; margin: 0.6rem 0 1.4rem; font-size: 0.9375rem; }
/* Sticky, because 58 rows is ~2.5 screens and scrolling otherwise leaves
   ms / 24h / 7d / 30d as four columns of unlabelled numbers. border-collapse drops
   borders off sticky cells, hence the inset shadow. */
thead th {
  text-align: left; font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace;
  font-size: 0.8125rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--ink-dim); font-weight: 600; padding: 10px 12px;
  position: sticky; top: 56px; background: var(--bg); z-index: 10;
  border-bottom: 0; box-shadow: inset 0 -1px 0 var(--line);
}
thead th.right { text-align: right; }
tbody td { padding: 6px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
/* Striping, because hover alone will not hold a row across 1,400px and nine columns.
   Class-driven rather than :nth-child, because sorting reorders rows and filtering
   hides them — :nth-child would count hidden rows and scramble the stripes. */
#board tbody tr:nth-child(even) { background: color-mix(in srgb, var(--ink) 3%, transparent); }
#board.sorted tbody tr { background: transparent; }
#board.sorted tbody tr.alt { background: color-mix(in srgb, var(--ink) 3%, transparent); }
#board tbody tr:hover, #board.sorted tbody tr:hover { background: var(--bg-hover); }

/* Sortable headers */
table.sortable thead th[data-sort] { cursor: pointer; user-select: none; padding-right: 20px; position: sticky; }
table.sortable thead th[data-sort]:hover { color: var(--ink); }
table.sortable thead th[data-sort]::after {
  content: "↕"; position: absolute; right: 6px; opacity: 0.3; font-size: 0.9em; font-weight: 400;
}
table.sortable thead th[data-dir="asc"]::after  { content: "↑"; opacity: 1; color: var(--link); }
table.sortable thead th[data-dir="desc"]::after { content: "↓"; opacity: 1; color: var(--link); }
table.compact tbody td { padding: 6px 10px; }
tbody tr:hover { background: var(--bg-hover); box-shadow: inset 2px 0 0 var(--line-hi); }

.row-down, .row-open {
  background: color-mix(in srgb, var(--bad) 9%, transparent);
  box-shadow: inset 3px 0 0 var(--bad);
}
.row-down:hover, .row-open:hover { background: color-mix(in srgb, var(--bad) 14%, transparent); box-shadow: inset 3px 0 0 var(--bad); }

/* The target cell is otherwise sized by its worst case, leaving a large gap before
   Client on every other row. */
td.target { max-width: 36ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
td.target .path { color: var(--ink-dim); }
/* Table links are --ink, not lime. The row hover and cursor are the affordance. */
td a { color: var(--ink); }
td a:hover { color: var(--link); text-decoration: underline; }

.tablehead { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.tablehead > div { display: flex; gap: 8px; align-items: center; }

/* ── Pills and dots ──────────────────────────────────────────────────────── */

.pill { display: inline-block; font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace; font-size: 0.75rem; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; padding: 3px 8px; border-radius: 10px; border: 1px solid var(--line-input); color: var(--ink-dim); vertical-align: middle; }
/* Health. Colour IS the fast identifier here, which is the whole job of this column
   — scanning 56 rows for the one that is wrong. The earlier concern about green was
   that lime links sat beside mint chips and the two yellow-greens collapsed; table
   links are now --ink, so that no longer applies. DOWN is filled so it still wins. */
.pill-up { background: color-mix(in srgb, var(--ok) 16%, transparent); border-color: var(--ok); color: var(--ok); font-weight: 600; }
.pill-down { background: var(--bad); border-color: var(--bad); color: var(--bad-on); font-weight: 700; }
.pill-unknown { border-color: var(--line-hi); color: var(--ink-dimmer); }
/* Monitoring column: green when it is genuinely being checked. */
.pill-active { border-color: color-mix(in srgb, var(--ok) 55%, transparent); color: var(--ok); }
/* Monitoring lifecycle. Green = being checked, amber = awaiting a human decision. */
.pill-pending   { border-color: var(--warn);   color: var(--warn); }
.pill-orphaned  { border-color: var(--warn);   color: var(--warn); }
.pill-paused    { border-color: var(--line);   color: var(--ink-dimmer); }
.pill-muted     { border-color: var(--warn);   color: var(--warn); }
.pill-confirmed { border-color: var(--bad);    color: var(--bad); }
.pill-degraded  { border-color: var(--warn);   color: var(--warn); }
.pill-note      { border-color: var(--line);   color: var(--ink-dimmer); }
.pill-dev       { border-color: var(--warn);   color: var(--warn); }
/* Audit actions stay neutral: colouring every history row makes a mundane trail a
   rainbow and drains meaning from colour elsewhere. */
.pill-delete, .pill-orphan, .pill-activate, .pill-create,
.pill-mute, .pill-unmute, .pill-pause, .pill-update, .pill-acknowledge {
  border-color: var(--line); color: var(--ink-dim);
}

.dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--ink-dimmer); }
.dot-up { background: var(--ok); }
.dot-down { background: var(--bad); box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 22%, transparent); }
.dot-unknown { background: var(--ink-dimmer); }

/* Was background:var(--bg) with color:var(--accent-on) — black on black in dark
   mode, so the pending count was invisible on the default landing page. */
.badge { display: inline-block; background: var(--accent); color: var(--accent-on); border: 1px solid var(--accent); border-radius: 8px; font-size: 0.72rem; font-weight: 700; padding: 1px 6px; margin-left: 4px; font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace; }
.on .badge { background: var(--accent-on); color: var(--accent); border-color: var(--accent-on); }

/* ── Forms and buttons ───────────────────────────────────────────────────── */

button, .button { font-family: inherit; font-size: 0.9375rem; font-weight: 600; padding: 7px 13px; border-radius: 4px; border: 1px solid var(--line-input); background: var(--bg-card); color: var(--ink); cursor: pointer; }
button:hover, .button:hover { background: var(--bg-hover); border-color: var(--ink-dimmer); text-decoration: none; }
button.primary { background: var(--accent); color: var(--accent-on); border-color: var(--accent); }
button.primary:hover { filter: brightness(1.08); }
button.danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 45%, transparent); }
button.danger:hover { background: color-mix(in srgb, var(--bad) 12%, transparent); }
button.small, .button.small { font-size: 0.875rem; padding: 5px 10px; }
button.link { background: none; border: none; color: var(--ink-dim); padding: 0; font-weight: 400; font-size: 0.875rem; text-decoration: underline; }
button.link:hover { color: var(--ink); background: none; }
form.inline { display: inline-block; }

label { display: block; font-size: 0.875rem; color: var(--ink-dim); margin-bottom: 2px; }
input, select, textarea { font-family: inherit; font-size: 0.9375rem; width: 100%; padding: 7px 9px; margin-top: 3px; background: var(--bg); color: var(--ink); border: 1px solid var(--line-input); border-radius: 4px; }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--link); outline-offset: 1px; border-color: var(--link); }
textarea { resize: vertical; }
input[type=search] { width: 260px; }
label.cb { display: flex; align-items: center; gap: 8px; color: var(--ink); }
label.cb input { width: auto; margin: 0; }
.stack { display: flex; flex-direction: column; gap: 11px; }
/* Without this, flex `stretch` made every submit in a .stack full width, so Mute,
   Save changes and Don't monitor all read as primary actions. */
.stack > button { align-self: start; }
.row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 11px; }
.checks { display: flex; flex-direction: column; gap: 7px; margin: 4px 0; }
.actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 0.5rem; }

/* ── Cards and layout ────────────────────────────────────────────────────── */

.card { background: var(--bg-card); border: 1px solid var(--line); border-radius: 4px; padding: 16px 18px; margin: 14px 0; }
.card.warn { border-left: 3px solid var(--warn); }
.cols { display: grid; gap: 16px; align-items: start; }
/* A fixed key/value list does not want the same width as a chart. */
@media (min-width: 900px) { .cols { grid-template-columns: 20rem minmax(0, 1fr); } }
.reviewgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
.pagehead { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.pagehead h1 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pagehead-meta { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; align-self: center; }
details.card summary { cursor: pointer; list-style: none; }
details.card summary::-webkit-details-marker { display: none; }
details.card summary h2 { display: inline; }
details.card summary::before { content: "▸ "; color: var(--ink-dim); }
details.card[open] summary::before { content: "▾ "; }

dl.kv { display: grid; grid-template-columns: auto 1fr; gap: 7px 16px; font-size: 0.9375rem; }
dl.kv dt { color: var(--ink-dim); }
ul.reasons { margin: 0.5rem 0 1rem 1.1rem; color: var(--ink-dim); font-size: 0.9375rem; }
ul.reasons li { margin-bottom: 0.5rem; max-width: 110ch; }

/* ── Down-state banner on the target page ────────────────────────────────── */

.downbox h2 { font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--bad); font-weight: 700; margin: 0 0 0.5rem; }
.downbox .why { font-size: 1.25rem; font-weight: 600; line-height: 1.35; margin-bottom: 0.4rem; }
.downbox .detail { color: var(--ink-dim); font-size: 0.9375rem; margin-bottom: 0.9rem; }

/* ── Sparkline ───────────────────────────────────────────────────────────── */

.spark { position: relative; height: 72px; background: var(--bg); border: 1px solid var(--line); border-radius: 3px; margin: 8px 0 4px; overflow: hidden; }
.spark .bar { position: absolute; bottom: 0; background: var(--spark); }
.spark .bar:hover { background: var(--ink); }
.spark .bar.bad { background: var(--bad); }
.spark-scale { display: flex; justify-content: space-between; color: var(--ink-dim); font-size: 0.8125rem; }
.spark-none { color: var(--ink-dim); font-size: 0.9375rem; }
.spark-none .vals { font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace; color: var(--ink); }

/* ── Login ───────────────────────────────────────────────────────────────── */

body.centred { display: grid; place-items: center; min-height: 100vh; }
.login { width: min(360px, 92vw); }
.login h1 { margin: 0 0 1rem; font-size: 1.3rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 800; }
.login h1 span { color: var(--link); }
.login button { width: 100%; margin-top: 12px; }
.login label { margin-bottom: 10px; }

/* ── Footer ──────────────────────────────────────────────────────────────── */

.foot { border-top: 1px solid var(--line); margin-top: 3rem; padding: 16px 24px 40px; color: var(--ink-dim); font-size: 0.875rem; max-width: 1400px; }
.foot p { max-width: 110ch; }

/* ── Theme toggle ────────────────────────────────────────────────────────── */

.theme-toggle { display: inline-flex; align-items: center; gap: 5px; background: var(--bg-card); border: 1px solid var(--line-input); border-radius: 4px; padding: 5px 10px; font-size: 0.85rem; color: var(--ink-dim); cursor: pointer; font-family: inherit; font-weight: 600; }
.theme-toggle:hover { background: var(--bg-hover); color: var(--ink); }
.theme-toggle svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle .tt-label { min-width: 2.6em; text-align: left; }

/* ── Dismiss control in the pending callout ──────────────────────────────── */

.dismiss { display: flex; gap: 6px; align-items: center; }
.dismiss input { width: 200px; font-size: 0.875rem; padding: 5px 8px; margin: 0; }
@media (max-width: 720px) { .dismiss { flex-direction: column; align-items: stretch; } .dismiss input { width: 100%; } }

/* ── Inline response-time bars ────────────────────────────────────────────────
   A number alone makes you read all 56 to find the slow one. A bar behind it makes
   the outlier obvious without reading anything. Scaled against a fixed 1500ms
   reference so rows are comparable, and clamped so a 2.5s outlier still renders. */
.msbar { position: relative; display: block; padding: 2px 0; }
.msbar .v { position: relative; z-index: 1; }
.msbar::before {
  content: ""; position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  height: 15px; width: calc(var(--pct) * 1%);
  background: color-mix(in srgb, var(--spark) 30%, transparent);
  border-radius: 2px;
}
.msbar.slow::before { background: color-mix(in srgb, var(--warn) 32%, transparent); }
.msbar.veryslow::before { background: color-mix(in srgb, var(--bad) 32%, transparent); }

/* Uptime figures: colour is the identifier, per the same reasoning as the state pill. */
.up-good { color: var(--ok); }
.up-warn { color: var(--warn); }
.up-bad  { color: var(--bad); font-weight: 600; }
.up-none { color: var(--ink-dimmer); }
