/* ═══════════════════════════════════════════════════════════════════════════
   RAS design system. One stylesheet for every RAS web surface.

   Rewritten 20 Aug 2026 from ras-research/DESIGN-BRIEF.md. The dark theme it
   replaces is in git history. Three things changed and each was forced by
   evidence rather than taste:

   WHITE GROUND. Dobres et al. 2017 (Applied Ergonomics), glance-like reading —
   the task a tech does on a ladder: under dark conditions white-on-black was
   dramatically worse; under bright conditions polarity made no significant
   difference (p = 0.665). Piepenbrock 2013: light mode won for both 18-33 and
   60-85 year olds, and participants performed better in light mode WITHOUT
   perceiving any difference, so preference is not evidence here. ISA-101, the
   process-plant HMI standard, also says avoid black backgrounds because
   "glare causes fatigue".

   NEVER COLOUR ALONE. Every state carries colour + named state + glyph +
   sentence. Required by ISA-101, ISO 22324 and WCAG 1.4.1, and forced by
   arithmetic: adjacent luminance gaps in this palette are about 0.012, which
   is not distinguishable in print, and a health officer may print the
   compliance record. Colour cannot carry state and neither can luminance.

   SEVERITY BY FILL, NOT HUE. On white an "amber" must go dark to be readable,
   and dark amber is indistinguishable from dark red under red-green colour
   blindness. Four ambers were tested against four reds; every pair failed the
   separation floor. So the urgent thing is the only FILLED block on the page.
   That survives greyscale, print and every CVD type.

   The words for each state live in shared/states.py, not here. This file
   styles them; it does not name them.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{
  /* ── one surface ────────────────────────────────────────────────────────
     There is no second surface and no dark mode. A raised panel is made by a
     hairline and space, not by a different grey. */
  --paper:#FFFFFF;
  --ink:#14110F;        /* body text        18.8:1 on white */
  --dim:#5C534D;        /* secondary         7.5:1 */
  --faint:#8A817B;      /* tertiary          3.8:1, large text and rules only */
  --line:#E4DFDA;       /* hairlines */
  --wash:#F7F5F3;       /* the only tint, for table headers and code */

  /* ── the state palette ──────────────────────────────────────────────────
     Computed and verified against white, not picked. Ratios in the comments
     are WCAG on #FFFFFF and are asserted in tests/shared/test_states.py, so a
     "nicer" colour cannot be substituted without the suite failing.

     A plausible amber #B76E00 FAILS at 4.00:1. It is not in this palette and
     the test pins that it stays out. */
  --fault:#A31515;      /* Needs a technician   7.85:1 */
  --warn:#8A5300;       /* Degrading            6.33:1 */
  --advisory:#2B4C7E;   /* Worth a look         8.61:1 */
  --ok:#2E7D32;         /* Normal               5.13:1 */
  --nodata:#6B7280;     /* Not reporting        4.83:1 */
  /* The brief proposed #9CA3AF. Recomputed: 2.54:1, which fails even the
     3:1 floor WCAG 1.4.11 sets for a graphical object, and this colour
     draws a glyph. Darkened to the nearest value that passes. */
  --new:#7F8792;        /* Just installed        3.63:1 */

  /* ── interaction ────────────────────────────────────────────────────────
     One colour means "you can click this" and it is used for nothing else. */
  --link:#1668C4;       /* 5.51:1 */

  /* ── brand ──────────────────────────────────────────────────────────────
     Copper is refrigeration pipe, which is a real material association nobody
     in the category has claimed. But it sits 7.2 degrees in hue from the
     reserved watch state and fails AA on white at 3.79:1.

     So: copper goes on the box, the label, the van and the marketing.
     Interface chrome is near-black. COPPER APPEARS NOWHERE IN THE DATA
     DISPLAY. The darkened #8A5522 below is for the wordmark only. */
  --copper:#8A5522;     /* 6.17:1 — logo lockup only */

  /* ── staff ──────────────────────────────────────────────────────────────
     Admin surfaces span every tenant. This violet is used for that one thing
     and appears nowhere else in the product. */
  --staff:#5B3FA8;

  --r:12px; --r-sm:8px;
}
*{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{background:var(--paper);color:var(--ink);
  font:17px/1.6 Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,
       Helvetica,Arial,sans-serif;
  -webkit-font-smoothing:antialiased;min-height:100vh}
a{color:var(--link);text-decoration:none}
a:hover{text-decoration:underline}
a:focus-visible,button:focus-visible,input:focus-visible,select:focus-visible,
summary:focus-visible,tr:focus-visible{
  outline:2px solid var(--link);outline-offset:2px;border-radius:4px}

/* ── type: three sizes on the whole product, not seven ──────────────────── */
h1,h2,h3,.fig,.hero{font-family:Sora,Inter,-apple-system,sans-serif;
  letter-spacing:-.02em}
.hero{font-size:40px;line-height:1.2;font-weight:700}
h1{font-size:30px;line-height:1.25;font-weight:700}
h2,.eyebrow{font-size:13px;font-weight:600;color:var(--faint);
  letter-spacing:.1em;text-transform:uppercase}
h2{margin:44px 0 14px}
h3{font-size:20px;font-weight:700}
.sub,.note{color:var(--dim);max-width:62ch}
.tiny{color:var(--faint);font-size:14px}
.crumbs{font-size:15px;color:var(--faint);margin-bottom:12px}

/* ── chrome ─────────────────────────────────────────────────────────────── */
header{display:flex;align-items:center;gap:20px;padding:20px 0;
  border-bottom:1px solid var(--line);margin-bottom:40px;flex-wrap:wrap}
.logo{display:flex;align-items:center;gap:9px;font-family:Sora,Inter,sans-serif;
  font-weight:700;font-size:18px;letter-spacing:.14em;color:var(--copper)}
.logo .wordmark{color:var(--ink)}
.mark{flex:none;display:block}
.tenant{color:var(--dim);font-size:15px}
header nav{margin-left:auto;display:flex;align-items:center;gap:20px;
  font-size:16px}
header nav a{color:var(--dim)}
header nav a:hover,header nav a.on{color:var(--ink);text-decoration:none}
header form{display:inline}
main{max-width:760px;margin:0 auto;padding:0 28px 90px}
main.wide{max-width:1240px}
footer{color:var(--faint);font-size:14px;text-align:center;padding:30px}
section{margin-bottom:48px}

/* ── the staff bar ──────────────────────────────────────────────────────── */
.staffbar{background:#F2EEFB;border-bottom:1px solid var(--line);
  color:var(--staff);font-size:13px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;padding:9px 28px;display:flex;gap:14px;
  flex-wrap:wrap;align-items:center}
.staffbar em{color:var(--dim);font-weight:400;font-style:normal;
  letter-spacing:0;text-transform:none}

/* ── buttons ────────────────────────────────────────────────────────────── */
/* 76dp is Google's DRIVING standard and it is the floor for a field surface.
   NN/g's absolute minimum is 1cm physical; the average thumb contact patch is
   2.5cm, two and a half times that. Technicians work one-handed because the
   other hand holds a tool. */
button,.btn{background:var(--link);border:0;color:#fff;font-weight:600;
  padding:14px 24px;border-radius:var(--r-sm);cursor:pointer;font-size:16px;
  min-height:48px;display:inline-flex;align-items:center;justify-content:center;
  font-family:inherit}
button:hover,.btn:hover{filter:brightness(1.08);text-decoration:none}
button.ghost,.btn.ghost{background:transparent;color:var(--link);
  border:1px solid var(--line)}
button[disabled],.btn.disabled{background:var(--wash);color:var(--faint);
  cursor:not-allowed;filter:none;border:1px solid var(--line)}
/* A disabled action must say WHY next to it. A greyed button with no
   explanation is a dead end. */
.btn-why{color:var(--dim);font-size:15px;margin-top:10px}
.btnrow{display:flex;gap:12px;flex-wrap:wrap;align-items:center}

/* ── the headline ───────────────────────────────────────────────────────────
   One computed sentence, no unit named. Eight rules, eight strings, in
   shared/states.py. */
.headline{font-family:Sora,Inter,sans-serif;font-size:40px;line-height:1.2;
  font-weight:700;letter-spacing:-.025em}
.headline .quiet{color:var(--dim)}
.showing-work{color:var(--dim);margin-top:14px;max-width:62ch}

/* ── the urgent block: the ONLY filled thing on the page ────────────────── */
.act{background:var(--fault);color:#fff;border-radius:var(--r);padding:30px 32px}
.act.warn{background:var(--warn)}
.act .where{font-size:14px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;opacity:.85}
.act .verdict{font-family:Sora,Inter,sans-serif;font-size:30px;font-weight:700;
  margin-top:8px;letter-spacing:-.02em}
.act p{margin-top:12px;max-width:52ch;opacity:.96}
.act .rd{display:flex;gap:34px;margin-top:24px;padding-top:20px;flex-wrap:wrap;
  border-top:1px solid rgba(255,255,255,.3)}
.act .rd .k{font-size:14px;opacity:.85}
.act .rd .v{font-family:Sora,Inter,sans-serif;font-size:24px;font-weight:700;
  margin-top:2px}
.act .rd .n{font-size:14px;opacity:.9;margin-top:2px}
.act .btns{margin-top:26px;display:flex;gap:12px;flex-wrap:wrap}
.act .btns a{background:#fff;color:var(--fault);font-weight:600;padding:14px 24px;
  border-radius:var(--r-sm);min-height:48px;display:inline-flex;
  align-items:center}
.act.warn .btns a{color:var(--warn)}
.act .btns a.o{background:transparent;color:#fff;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.55)}

/* ── a secondary concern: outlined, not filled ──────────────────────────── */
.watch{border:1px solid var(--line);border-left:4px solid var(--warn);
  border-radius:var(--r-sm);padding:20px 24px}
.watch.advisory{border-left-color:var(--advisory)}
.watch.nodata{border-left-color:var(--nodata)}
.watch .lbl{font-size:13px;font-weight:600;letter-spacing:.08em;
  text-transform:uppercase;color:var(--warn)}
.watch.advisory .lbl{color:var(--advisory)}
.watch.nodata .lbl{color:var(--nodata)}
.watch .t{font-weight:600;font-size:19px;margin-top:4px}
.watch p{color:var(--dim);margin-top:4px;max-width:56ch}

/* ── the state chip: colour + glyph + word, always all three ────────────── */
.chip{display:inline-flex;align-items:center;gap:7px;font-size:15px;
  font-weight:600;white-space:nowrap}
.chip .g{font-size:13px;line-height:1}
.chip.s-OK{color:var(--ok)} .chip.s-NEW{color:var(--dim)}
.chip.s-NODATA{color:var(--nodata)} .chip.s-PENDING{color:var(--nodata)}
.chip.s-NOT_INSTALLED{color:var(--nodata)}
.chip.s-ADVISORY{color:var(--advisory)} .chip.s-WARN{color:var(--warn)}
.chip.s-FAULT{color:var(--fault)}

/* ── the unit list. A LIST, not cards and not a table ───────────────────────
   Lists are for scanning and this is a scanning task. Worst first, never
   alphabetical. */
.units{border-top:1px solid var(--line)}
.units a,.units div.row{display:flex;align-items:baseline;gap:16px;
  padding:16px 0;border-bottom:1px solid var(--line);color:var(--ink);
  min-height:56px}
.units a:hover{background:var(--wash);text-decoration:none}
.units .nm{font-weight:600}
.units .st{color:var(--faint);font-size:15px}
.units .rt{margin-left:auto;text-align:right;font-variant-numeric:tabular-nums;
  color:var(--dim)}

/* ── tables: the fleet console and the record, not the customer overview ── */
.tablewrap{border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
table{width:100%;border-collapse:collapse}
th{background:var(--wash);text-align:left;font-size:12px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--dim);font-weight:600;padding:12px 14px;
  white-space:nowrap}
td{padding:14px;border-top:1px solid var(--line);font-size:16px;
  vertical-align:top}
tr.rowlink{cursor:pointer}
tr.rowlink:hover td{background:var(--wash)}
.num{font-variant-numeric:tabular-nums}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:15px}

/* ── empty states are designed, not defaulted ───────────────────────────────
   With a target of zero alerts in a normal month, the empty screen IS the
   customer's normal experience. "Nothing wrong", "nothing yet" and "nothing
   visible" are three different messages and must never share one. */
.empty{border:1px dashed var(--line);border-radius:var(--r);padding:30px 26px}
.empty .t{font-weight:600;font-size:19px;margin-bottom:6px}
.empty .d{color:var(--dim);max-width:60ch}

/* ── forms ──────────────────────────────────────────────────────────────── */
label{display:block;font-size:15px;color:var(--dim);margin:16px 0 6px}
input,select,textarea{width:100%;padding:14px 13px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--paper);color:var(--ink);
  /* 16px minimum or iOS Safari zooms the page on focus, and half of these
     forms are filled in on a phone. */
  font-size:16px;font-family:inherit;min-height:48px}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--link)}
input[type=checkbox],input[type=radio]{width:auto;min-height:0}
fieldset{border:1px solid var(--line);border-radius:var(--r);
  padding:6px 20px 20px;margin:20px 0}
legend{color:var(--dim);font-size:12px;font-weight:600;text-transform:uppercase;
  letter-spacing:.1em;padding:0 8px}
.formrow{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:0 18px}
.subcard{background:var(--wash);border:1px solid var(--line);
  border-radius:var(--r-sm);padding:6px 16px 16px;margin:14px 0}
.auth{max-width:420px;margin:8vh auto 0;border:1px solid var(--line);
  border-radius:var(--r);padding:32px}
.auth button{width:100%;margin-top:22px}
.auth .links{margin-top:18px;font-size:15px;color:var(--dim);text-align:center}

.error{border:1px solid var(--fault);border-left:4px solid var(--fault);
  color:var(--fault);border-radius:var(--r-sm);padding:14px 16px;
  margin-bottom:10px}
.notice{border:1px solid var(--line);border-left:4px solid var(--ok);
  color:var(--ink);border-radius:var(--r-sm);padding:14px 16px;margin-bottom:10px}
.warnbox{border:1px solid var(--line);border-left:4px solid var(--warn);
  color:var(--ink);border-radius:var(--r-sm);padding:14px 16px;margin-bottom:10px}

/* ── report / detail ────────────────────────────────────────────────────── */
.report{border:1px solid var(--line);border-radius:var(--r);padding:24px}
.report dt{color:var(--dim);font-size:12px;text-transform:uppercase;
  letter-spacing:.1em;font-weight:600;margin-top:22px}
.report dd{margin-top:6px}
.report ul,.report ol{margin:8px 0 0 20px}
.report li{margin-bottom:8px}

.chartbox{border:1px solid var(--line);border-radius:var(--r);padding:18px;
  margin-bottom:16px}
.chartbox h3{font-size:13px;color:var(--dim);margin-bottom:12px;
  text-transform:uppercase;letter-spacing:.1em;font-weight:600}

/* ── phone ──────────────────────────────────────────────────────────────────
   Half of these opens are on a phone, often on a roof, one-handed. Primary
   actions sit in the bottom third for thumb reach; alarm text stays at the
   top beside the parameter. */
@media (max-width:640px){
  body{font-size:16px}
  main{padding:0 18px 70px}
  .hero,.headline{font-size:28px}
  h1{font-size:24px}
  header{padding:14px 0;gap:12px}
  header nav{gap:14px;width:100%;margin-left:0;order:3}
  .act{padding:22px 20px}
  .act .verdict{font-size:24px}
  .act .rd{gap:20px}
  .staffbar{padding:9px 18px}
  .tablewrap{border:0}
  thead{display:none}
  tbody tr{display:block;border:1px solid var(--line);border-radius:var(--r);
    padding:14px 16px;margin-bottom:12px}
  tbody td{display:flex;justify-content:space-between;gap:16px;padding:5px 0;
    border:0}
  tbody td::before{content:attr(data-label);color:var(--dim);font-size:13px;
    text-transform:uppercase;letter-spacing:.07em;font-weight:600;flex:0 0 auto}
  tbody td[data-label=""]::before{display:none}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important;
  transition:none!important}}

/* ── print ──────────────────────────────────────────────────────────────────
   A compliance record gets printed and handed to an environmental health
   officer. It must survive black and white, which is why every state carries a
   glyph: adjacent luminance gaps here are about 0.012 and vanish in print. */
@media print{
  body{font-size:11pt}
  header nav,.staffbar,button,.btn,.toolbar,footer{display:none!important}
  main{max-width:none;padding:0}
  .tablewrap,.report,.chartbox,.empty{border-color:#999;break-inside:avoid}
  thead{display:table-header-group}
  a{color:#000;text-decoration:none}
  .act{background:none!important;color:#000!important;
    border:2px solid #000;border-radius:0}
  .act .btns{display:none}
  .printonly{display:block!important}
}
.printonly{display:none}

/* ═══════════════════════════════════════════════════════════════════════════
   RESTORED 20 Aug 2026, and worth saying why rather than just adding them.

   The rewrite above replaced the dark theme wholesale and dropped every class
   it did not itself use. The templates still used thirty-five of them, so the
   compliance record, the fault report, the unit page and the gateway list have
   been rendering partly unstyled since — tiles with no box, verdicts with no
   dot, tables with no toolbar. It reads as "unfinished", which on the one page
   an environmental health officer sees is expensive.

   The block below restores the ones the temperature record and the fault
   report need. NO NEW COLOUR IS INTRODUCED: every rule here reuses a token
   defined at the top of this file, whose contrast against white is computed
   and asserted in tests/shared/test_states.py. tests/shared/test_theme.py
   asserts that no hex value appears outside the palette, so this stays true.

   Still outstanding, and belonging with the P3 unit-detail rebuild rather than
   here: gw-*, rm-*, sp-*, u*, add-unit, saddr, sname, site, unit.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── the verdict block: a state, its glyph and its sentence ──────────────── */
.verdict{display:flex;gap:14px;align-items:flex-start;border:1px solid var(--line);
  border-left:4px solid var(--dim);border-radius:var(--r-sm);padding:18px 20px}
.verdict .dot{flex:none;width:12px;height:12px;border-radius:50%;
  background:var(--dim);margin-top:7px}
.verdict .headline{font-family:Sora,Inter,sans-serif;font-size:20px;
  font-weight:700;letter-spacing:-.01em;line-height:1.3}
.verdict .detail{color:var(--dim);font-size:15px;margin-top:3px}
.verdict.ok{border-left-color:var(--ok)}
.verdict.ok .dot{background:var(--ok)}
.verdict.warn{border-left-color:var(--warn)}
.verdict.warn .dot{background:var(--warn)}
.verdict.bad{border-left-color:var(--fault)}
.verdict.bad .dot{background:var(--fault)}

/* ── tiles: a small number with its unit and its name ────────────────────── */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:12px}
.tile{border:1px solid var(--line);border-radius:var(--r-sm);padding:14px 16px}
.tile .k{font-size:12px;letter-spacing:.09em;text-transform:uppercase;
  color:var(--dim);font-weight:600}
.tile .v{font-family:Sora,Inter,sans-serif;font-size:26px;font-weight:700;
  margin-top:4px;font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.tile .u{font-size:15px;font-weight:600;color:var(--dim)}
.tile .note{font-size:13px;color:var(--dim);margin-top:6px;line-height:1.45}
/* Severity by border, not by fill. A wall of coloured boxes is a wall; one
   marked edge is a signal. */
.tile.bad{border-color:var(--fault);border-left-width:4px}
.tile.warn{border-color:var(--warn);border-left-width:4px}
.tile.ok{border-color:var(--ok);border-left-width:4px}

/* ── card: a labelled statement of fact ─────────────────────────────────── */
.card{border:1px solid var(--line);border-radius:var(--r-sm);padding:16px 18px}
.card .name{font-weight:600}
.card .meta{color:var(--dim);font-size:15px;margin-top:4px;max-width:70ch}

/* ── toolbar: controls above a record, hidden when printed ──────────────── */
.toolbar{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;
  margin-bottom:18px}
.toolbar .spacer{flex:1 1 auto}
.toolbar label{margin-top:0}

/* ── small text roles ───────────────────────────────────────────────────── */
.hint{color:var(--dim);font-size:15px}
.dim{color:var(--dim)}
.lede{font-size:19px;line-height:1.55;max-width:62ch}
.attn{color:var(--fault);font-weight:600}
.pill{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);
  border-radius:999px;padding:4px 12px;font-size:13px;font-weight:600;
  color:var(--dim)}
.pill.tight{padding:3px 10px}
.pill.staff{color:var(--staff);border-color:var(--staff)}

/* ═══════════════════════════════════════════════════════════════════════════
   The temperature record's chart.

   Server-rendered SVG, not a canvas: it prints at print resolution, it needs
   no JavaScript on a phone in a kitchen, and the same geometry goes into the
   PDF. See shared/tempchart.py.

   NEVER COLOUR ALONE lands here in an unexpected place. The safe band is a
   light FILL plus a BOUNDING LINE, because an officer may print this in black
   and white and a fill alone is a grey wash with no edge. The excursion is a
   tinted region plus a marked peak plus a label that states its duration —
   three channels, none of them hue.
   ═══════════════════════════════════════════════════════════════════════════ */
.chart{border:1px solid var(--line);border-radius:var(--r);padding:14px 12px 6px;
  margin:14px 0}
.trace{display:block;width:100%;height:auto;overflow:visible}
.trace .safe{fill:var(--wash)}
.trace .limit{stroke:var(--ok);stroke-width:1.5}
.trace .limitlab{fill:var(--ok);font-size:12px;font-weight:600}
.trace .grid{stroke:var(--line);stroke-width:1}
.trace .tick{fill:var(--dim);font-size:11px}
.trace .axtitle{fill:var(--dim);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;font-weight:600}
.trace .line{fill:none;stroke:var(--ink);stroke-width:1.8;
  stroke-linejoin:round;stroke-linecap:round}
/* Same token as the fault state, at low opacity. An opacity is not a new
   colour, which is why this block adds none. */
.trace .exc{fill:var(--fault);opacity:.09}
.trace .peak{fill:var(--fault)}
.trace .exclab{fill:var(--fault);font-size:12px;font-weight:700}

@media print{
  /* The band's edge and the excursion's boundary must survive a mono printer,
     where the two fills flatten to nearly the same grey. */
  .trace .limit{stroke:#000;stroke-width:2}
  .trace .exc{opacity:.18}
  .chart{border-color:#999;break-inside:avoid}
}

/* ═══════════════════════════════════════════════════════════════════════════
   The unit detail reading row (P3, 20 Aug 2026).

   ISA-101's implementation spec, laid out: name, live value, sparkline, banded
   analog bar, and the interpretation underneath. The alarm text for a channel
   sits inside that channel's own block — clinical usability testing found
   users prefer alarm messages "within the parameter area and parameter value
   area" rather than in a separate log.

   No new colour. Every rule reuses a token from :root.
   ═══════════════════════════════════════════════════════════════════════════ */
.reads{border-top:1px solid var(--line)}
.read{display:grid;gap:2px 16px;padding:14px 0;border-bottom:1px solid var(--line);
  grid-template-columns:minmax(140px,1fr) auto auto minmax(190px,auto);
  grid-template-areas:"k v s b" "n n n n" "f f f f";align-items:center}
.read .rk{grid-area:k;font-weight:600}
.read .rv{grid-area:v;font-family:Sora,Inter,sans-serif;font-size:22px;
  font-weight:700;letter-spacing:-.02em;white-space:nowrap}
.read .rv .u{font-size:14px;font-weight:600;color:var(--dim)}
.read .rs{grid-area:s;line-height:0}
.read .rb{grid-area:b;line-height:0}
.read .rn{grid-area:n;color:var(--dim);font-size:15px;margin-top:4px}
.read .rn .raw{color:var(--faint)}
/* The fault message, inside the parameter's own block. */
.read .rf{grid-area:f;margin-top:8px;padding:9px 12px;border-radius:var(--r-sm);
  border:1px solid var(--fault);border-left-width:4px;font-size:15px}
.read .rf a{font-weight:600}
/* Severity by edge, never by filling the row: a page of coloured rows is a
   page, and the urgent thing has to be the only filled block on it. */
.read.bad .rk{color:var(--fault)}
.read.warn .rk{color:var(--warn)}

/* ── the analog bar ─────────────────────────────────────────────────────── */
.bar{display:block}
.bar .track{fill:var(--wash);stroke:var(--line);stroke-width:1}
.bar .band{fill:#FFFFFF;stroke:var(--ok);stroke-width:1}
.bar .edge{stroke:var(--ok);stroke-width:1.5}
.bar .mark,.bar .mark-line{fill:var(--ink);stroke:var(--ink);stroke-width:1.5}
.bar .mark.s-warn,.bar .mark-line.s-warn{fill:var(--warn);stroke:var(--warn)}
.bar .mark.s-bad,.bar .mark-line.s-bad{fill:var(--fault);stroke:var(--fault)}

/* ── the sparkline: a shape, not a chart ────────────────────────────────── */
.spark{display:block}
.spark .sl{fill:none;stroke:var(--dim);stroke-width:1.4;stroke-linejoin:round;
  stroke-linecap:round}
.spark .se{fill:var(--ink)}
.spark .se.s-warn{fill:var(--warn)}
.spark .se.s-bad{fill:var(--fault)}

/* ── the staff seam on a customer page ──────────────────────────────────── */
.staffonly{border:1px dashed var(--staff);border-radius:var(--r);
  padding:6px 18px 18px;margin-top:18px}
.staffonly .eyebrow{color:var(--staff);margin:14px 0 4px}

/* ── the gateway list and the remaining unit-page families ──────────────────
   The last of the classes the 20 Aug rewrite dropped. Same rule as the block
   above: no new colour, every value is a token. */
.gw-bar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:2px 0 8px}
.gw-id{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:15px;
  color:var(--dim)}
.gw-sub{color:var(--dim);font-size:15px}
.rf-wrap{margin-top:8px}
.rf-why{color:var(--dim);font-size:15px;margin-top:4px}
.rm-site,.rm-unit{color:var(--dim);font-size:15px}
.sp-wrap{display:flex;gap:12px;flex-wrap:wrap;align-items:baseline}
.uname{font-weight:600}
.uet,.urf,.usp{color:var(--dim);font-size:15px}
.sname{font-weight:600}
.saddr{color:var(--dim);font-size:15px}
.site,.unit{display:block}
.add-unit{margin-top:14px}

@media (max-width:640px){
  .read{grid-template-columns:1fr auto;
    grid-template-areas:"k v" "s s" "b b" "n n" "f f";gap:6px 12px}
  .read .rb,.read .rs{margin-top:4px}
  .bar{width:100%}
}
@media print{
  .staffonly{display:none!important}
  .bar .mark,.bar .mark-line{fill:#000;stroke:#000}
  .bar .band{stroke:#000}
}
