/* Sereno components: the stylesheet over tokens.css.

   The look is direction D2 (docs/design/mockups/ds2/): C's editorial page
   with a margin column and a lamp post, B's shared day axis at 30 days, and
   one status block at the top. The partials that emit these classes are the
   tyxml functions in lib/design (Sereno_design.*); see
   docs/systems/design-system.md.

   Layers, in this order: base; layout and shells (the public page, the
   operator pages); components (one section per partial); screens (only what
   is specific to one page). A component's narrow rules sit with it.

   Rules read semantic tokens only (--ground, --text, --status-*), never
   palette primitives or literal colours. Status colours go only on status
   selectors (.s-down, .d-degraded, ...), lantern gold only on the brand, the
   lantern and focus; test/design/test_brief.ml enforces this. Both public
   and operator pages load this file; custom domains serve it too. */

/* ==== Base ================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { font-feature-settings: "lnum" 1; }

:root {
  /* The public page's two columns: the margin, then the text. */
  --pub-gutter: 1rem;
  --pub-m: 14rem;
  --pub-p: 2.25rem;
  --pub-wide: 74rem;
  /* Day rows: 30 days at 13 px. */
  --strip: 390px;
  --days: 30;
}

.data {
  font-family: var(--font-data);
  font-size: var(--fs-data);
  font-variant-numeric: tabular-nums;
  /* A mono space is as wide as a digit, so "10 s" and "2 d 1 h" read as
     loose pieces: pull the words of a duration or a time together (UW,
     audit S10 and checks 2). */
  word-spacing: -0.2em;
}

/* The type scale (brief section 7): every size is a --fs-* token and every
   weight a --weight-* one; nothing is left to the browser's defaults (700
   for headings, bold and table heads). Plex Mono has two weights: regular,
   and medium inside semibold text. */
:where(h1, h2, h3, h4, h5, h6, b, strong, th, optgroup) { font-weight: var(--weight-strong); }
:where(.data, code, kbd, samp, pre) { font-weight: var(--weight-regular); }
:where(h1, h2, h3, h4, h5, h6, b, strong, th, .badge, .chip) :where(.data, code, kbd, samp) { font-weight: var(--weight-medium); }

.voice {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--weight-regular);
  font-size: var(--fs-voice);
  line-height: var(--lh-ui);
  color: var(--text-voice);
}
.voice time { font: inherit; }

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Controls take the page's type, not the browser's. */
input, select, textarea, button { font: inherit; }

/* The disclosure marker: every summary shows a chevron, drawn here from two
   borders at the icons' stroke weight, that turns down when its details
   open; no browser triangle. A partial that draws Icon.Chevron itself keeps
   its own. The marker is a flex item before the label, so it never wraps
   away from it. Summaries are touch targets (brief section 12). */
summary {
  display: flex;
  align-items: center;
  min-height: var(--touch-min);
  list-style: none;
  cursor: pointer;
}
summary::-webkit-details-marker { display: none; }
summary::before {
  content: "";
  flex: none;
  width: 0.39em;
  height: 0.39em;
  margin: 0 0.6em 0 0.2em;
  border: solid currentColor;
  border-width: 0 0.09em 0.09em 0;
  transform: rotate(-45deg);
}
details[open] > summary::before { transform: rotate(45deg); }
summary:has(.ico-chev)::before { content: none; }

/* ---- status (DS Q10) ------------------------------------------------------- */

/* Every status is a class that sets its colours: --c for fills and icons,
   --ct for words, --cb for tints. */

.s-ok       { --c: var(--status-ok);       --ct: var(--status-ok-text);       --cb: var(--status-ok-bg); }
.s-degraded { --c: var(--status-degraded); --ct: var(--status-degraded-text); --cb: var(--status-degraded-bg); }
.s-down     { --c: var(--status-down);     --ct: var(--status-down-text);     --cb: var(--status-down-bg); }
.s-maint    { --c: var(--status-maint);    --ct: var(--status-maint-text);    --cb: var(--status-maint-bg); }
.s-unknown  { --c: var(--status-unknown);  --ct: var(--status-unknown-text);  --cb: var(--status-unknown-bg); }

/* ---- the operator pages' plain elements ------------------------------------ */

/* What workspace pages share outside the partials: flash notices, fact
   lists, filter forms, and the profile's and status pages' forms and buttons.
   Operator pages only (.op); the elements carry no specificity of their own
   (:where), so every partial's rules win over them. */

:where(.op) .muted { color: var(--text-muted); }
:where(.op) dl.facts { display: grid; grid-template-columns: max-content 1fr; gap: .25rem 1rem; margin: 0 0 1rem; }
:where(.op) dl.facts dt { color: var(--text-muted); }
:where(.op) dl.facts dd { margin: 0; }
:where(.op) form { margin: 0; }
:where(.op) form.filters, :where(.op) .row { display: flex; flex-wrap: wrap; gap: .5rem 1rem; align-items: end; margin: 0 0 1rem; }
:where(.op) label { display: block; font-size: var(--fs-small); color: var(--text-muted); }
:where(.op) label.inline { display: inline-flex; gap: .3rem; align-items: center; color: var(--text); font-size: inherit; }
:where(.op) :is(input, select, textarea, button) { color: inherit; }
:where(.op) input[type=text], :where(.op) input[type=password], :where(.op) input:not([type]),
:where(.op) select, :where(.op) textarea {
  display: block; width: 100%; max-width: 32rem; padding: .3rem .4rem;
  background: var(--ground); border: 1px solid var(--border-strong); border-radius: var(--radius);
}
:where(.op) input[type=date] { display: block; padding: .3rem .4rem; background: var(--ground); border: 1px solid var(--border-strong); border-radius: var(--radius); }
:where(.op) textarea { min-height: 5rem; }
:where(.op) fieldset { border: 1px solid var(--border); border-radius: .3rem; margin: .5rem 0; }
:where(.op) ul.plain { list-style: none; padding: 0; margin: 0; }
:where(.op) ul.plain li { padding: .15rem 0; }
:where(.op) code { font-family: var(--font-data); font-size: var(--fs-data); overflow-wrap: anywhere; }

/* Touch targets (brief section 12): under 46rem every control on an
   operator page is at least 44 px tall, as the alert filters were first. */
@media (max-width: 46rem) {
  :where(.op) :is(select, button, textarea, input:not([type=checkbox], [type=radio], [type=hidden])) { min-height: var(--touch-min); }
  :where(.op) label.inline { min-height: var(--touch-min); }
  :where(.op) :is(th, td) a[href] { display: inline-flex; align-items: center; min-height: var(--touch-min); }
}

/* ==== Layout and shells ===================================================== */

/* ---- the public page: the frame, the masthead, the lamp post (D2) ---------- */

:where(.pub) :where(h1, h2, h3, h4, p, ul, ol) { margin: 0; }

.page {
  position: relative;
  max-width: calc(var(--pub-wide) + 2 * var(--pub-gutter));
  margin: 0 auto;
  padding: 0 var(--pub-gutter) var(--space-8);
}

/* The lamp post: from under the lantern to the footer. */
.pub .page::before {
  content: "";
  position: absolute;
  left: calc(var(--pub-gutter) + var(--pub-m));
  top: calc(var(--space-8) + 3.5rem);
  bottom: var(--space-8);
  border-left: var(--border-width-strong) solid var(--border-strong);
}

/* Two columns everywhere: the margin, then the main column. */
.mast, .lrow, .block, .foot {
  display: grid;
  grid-template-columns: var(--pub-m) minmax(0, 1fr);
}
.mast > *, .block > *, .foot > * { grid-column: 2; padding-left: var(--pub-p); }
.mast > .dateline, .block > .margin-head, .foot > .foot-margin {
  grid-column: 1;
  grid-row: 1;
  padding-left: 0;
  padding-right: var(--pub-p);
  text-align: right;
}

.mast { position: relative; padding-top: var(--space-8); align-items: start; }
.mast-mark {
  position: absolute;
  left: var(--pub-m);
  top: var(--space-8);
  width: 2.5rem;
  padding: 0;
  grid-column: auto;
  grid-row: auto;
  transform: translateX(-50%) translateX(0.75px);
}
.mast-mark .mark { display: block; width: 2.5rem; height: 3.125rem; }
.mast-title { grid-row: 1; padding-left: calc(var(--pub-p) + 0.75rem); }
.mast h1 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
}
.mast-title p { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--fs-small); }
.dateline {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-small);
  line-height: var(--lh-ui);
}

/* A block: a margin heading beside the main column. */

.block { padding-top: var(--space-10); align-items: start; }
.margin-head h2 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
}
.margin-note { margin: var(--space-1) 0 0; font-size: var(--fs-small); color: var(--text-muted); }
.days-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  margin-top: var(--space-2);
  font-size: var(--fs-small);
}

/* The footer. */

.foot { padding-top: var(--space-10); font-size: var(--fs-small); align-items: start; }
.foot-margin { padding-top: var(--space-3); }
.foot-margin p { margin: 0; font-family: var(--font-display); font-weight: var(--weight-strong); font-size: var(--fs-body); }
.foot-main {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-top: var(--space-3);
  border-top: var(--border-width-strong) solid var(--text);
}
.foot-main p { margin: 0; }
.links { display: flex; flex-wrap: wrap; gap: 0 var(--space-5); margin: 0; padding: 0; list-style: none; }
.links a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
.updated { color: var(--text-muted); }
.updated .data { font-size: var(--fs-small); }

/* Medium: the margin narrows. */
@media (max-width: 68rem) {
  :root { --pub-m: 11rem; --pub-p: 1.75rem; }
}

/* Narrow: the post moves to the left edge, and the margin notes fold into
   the main column. */
@media (max-width: 46rem) {
  :root { --pub-m: 1.25rem; --pub-p: 1.25rem; }
  .mast > .dateline, .block > .margin-head, .foot > .foot-margin {
    grid-column: 2;
    grid-row: auto;
    padding-left: var(--pub-p);
    padding-right: 0;
    text-align: left;
  }
  .mast-title { padding-left: calc(var(--pub-p) + 0.75rem); }
  .mast > .dateline {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0 var(--space-3);
    margin-top: var(--space-3);
    padding-left: calc(var(--pub-p) + 0.75rem);
  }
  .block { padding-top: var(--space-8); }
  .margin-head { margin-bottom: var(--space-4); }
  .margin-head h2 { display: inline; }
  .margin-note { display: inline; margin-left: var(--space-3); }
  .days-link { margin-top: 0; }
  .foot > .foot-margin { grid-row: 2; padding-top: var(--space-3); }
}

/* ---- the operator pages (night) -------------------------------------------- */

/* One frame for every operator page: the header, the main column and the
   footer share one content width (--op-wide) and one gutter (--op-gutter),
   so the lantern lines up with the headings at every width. Each of them
   pads itself to the frame (--op-inset) rather than sitting in a wrapper, so
   the header's surface still runs edge to edge. Pages never set their own
   width: a line that should stay short uses --measure. The scrollbar's
   space is always reserved, so short and long pages share the frame. */

:root {
  --op-wide: 74rem;
  --op-gutter: clamp(1rem, 4vw, 3rem);
  --op-inset: max(var(--op-gutter), calc((100% - var(--op-wide)) / 2));
}
:root:has(> body.op) { scrollbar-gutter: stable; }

.skip-link { position: absolute; left: -999px; display: inline-flex; align-items: center; min-height: var(--touch-min); }
.skip-link:focus {
  left: var(--space-2);
  top: var(--space-2);
  z-index: 2;
  padding: var(--space-1) var(--space-2);
  background: var(--surface);
  color: var(--text);
}

/* The header: the brand, the navigation and the session, in one row at
   every width. The navigation scrolls sideways when it doesn't fit (a fade
   at its end says there is more); under 46rem the session moves to the
   profile page. */
.op-head {
  display: flex;
  align-items: center;
  gap: 0 var(--space-6);
  padding: 0 var(--op-inset);
  background: var(--surface);
  border-bottom: var(--border-width) solid var(--border);
}
.op-brand {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  color: var(--text);
  text-decoration: none;
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
  line-height: 1;
}
.op-brand .lantern { width: 1.2rem; height: 1.5rem; }
.op-brand:hover { color: var(--text); }
.op-nav { flex: 1 1 auto; min-width: 0; }
.op-nav ul {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-x: auto;
  scrollbar-width: none;
}
.op-nav li { flex: none; }
.op-nav a {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  padding-inline: var(--space-2);
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.op-nav a:hover { color: var(--text); }
.op-nav a[aria-current="page"] { color: var(--text); font-weight: var(--weight-strong); box-shadow: inset 0 -2px 0 var(--text); }
/* The ring sits inside the item, so the scroller and the top of the window
   never clip it. */
.op-nav a:focus-visible { outline-offset: -3px; box-shadow: none; }
.op-nav a[aria-current="page"]:focus-visible { box-shadow: inset 0 -2px 0 var(--text); }
.op-who {
  display: flex;
  flex: none;
  align-items: center;
  gap: 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.op-who form { margin: 0; }
/* Sign out: a quiet link-style control, 44 px tall. */
.op-who button {
  min-height: var(--touch-min);
  margin: 0;
  padding: 0 0 0 var(--space-2);
  color: var(--link);
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.op-who button:hover { color: var(--link-hover); }

.op-main, .op-foot { padding-inline: var(--op-inset); }
.op-main { padding-block: var(--space-6) var(--space-8); }
.op-main h1 {
  margin: var(--space-2) 0 var(--space-5);
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h2);
  line-height: var(--lh-tight);
}
.op-main h2 {
  margin: var(--space-8) 0 var(--space-3);
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
}
.op-main h3 { margin: var(--space-6) 0 var(--space-2); font-size: var(--fs-ui); font-weight: var(--weight-strong); }
.op-foot {
  padding-block: 0 var(--space-6);
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.op-foot p { margin: 0; }

/* Links back to the lists a page belongs to, in a row. A trail (Crumbs)
   separates them with a drawn slash and ends with the page itself, cut to
   one line. */
.crumbs ul, .crumbs ol { display: flex; flex-wrap: wrap; gap: 0 var(--space-5); margin: 0; padding: 0; list-style: none; }
.crumbs a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
.crumbs.trail ol { flex-wrap: nowrap; gap: 0; }
.trail li { display: flex; align-items: center; flex: none; min-width: 0; }
.trail li + li::before {
  content: "";
  flex: none;
  width: 1px;
  height: 1em;
  margin: 0 var(--space-3);
  background: var(--text-muted);
  transform: rotate(20deg);
}
.trail .trail-here { flex: 0 1 auto; color: var(--text-muted); }
.trail-here span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The name gives way first; then the navigation scrolls, with a fade at its
   end and room to scroll the last item clear of it. */
@media (max-width: 72rem) {
  .op-who-name { display: none; }
}
@media (max-width: 64rem) {
  .op-nav ul {
    padding-inline-end: var(--space-8);
    mask-image: linear-gradient(to left, transparent, #000 var(--space-8));
  }
  /* Scrolled (hour-call.js): a fade at the start as well (re-audit N15). */
  .op-nav ul.is-scrolled {
    mask-image: linear-gradient(to right, transparent, #000 var(--space-6), #000 calc(100% - var(--space-8)), transparent);
  }
}
@media (max-width: 46rem) {
  .op-head { gap: 0 var(--space-3); }
  .op-who { display: none; }
  .op-main { padding-block: var(--space-4) var(--space-8); }
  .op-main h1 { font-size: var(--fs-h2-narrow); }
}

/* ==== Components ============================================================ */

/* ---- icons ----------------------------------------------------------------- */

/* One stroke weight, matching the lantern; coloured by currentColor. */

.ico {
  width: 1.125em;
  height: 1.125em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: square;
  stroke-linejoin: miter;
  vertical-align: -0.18em;
}
.ico-step { width: 1.7em; height: 0.55em; vertical-align: 0.05em; stroke-width: 1.25; }
.ico-chev { width: 1rem; height: 1rem; }
details[open] > summary .ico-chev { transform: rotate(90deg); }

.lantern {
  width: 1.25em;
  height: 1.5625em;
  color: var(--brand);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linejoin: miter;
}

/* ---- badges and ink states ------------------------------------------------- */

/* A status badge: icon and word, both in the status text colour. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: var(--ct);
  font-weight: var(--weight-strong);
  white-space: nowrap;
}
/* The word, not the icon, gives the badge its baseline, so a badge lines up
   with the text beside it (audit, checks 4). */
.badge > :not(svg), .loc > :not(svg) { align-self: baseline; }

/* Post and incident states are not statuses: ink text with an icon. */
.poststate, .incstate {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  color: var(--text);
  font-weight: var(--weight-strong);
}

/* ---- priority chips (Q14) -------------------------------------------------- */

.prio { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.5rem;
  padding: 0.05rem 0.45rem;
  border: var(--border-width-strong) solid var(--text);
  border-radius: var(--radius);
  font-size: var(--fs-small);
  font-weight: var(--weight-strong);
  line-height: 1.3;
  color: var(--text);
  white-space: nowrap;
}
/* An inactive P1 reads at least as heavy as P2: the same ink solid, with a
   2 px ring. Only an active P1 is red. */
.chip-p1 {
  color: var(--priority-p1-idle-text);
  background: var(--priority-p1-idle-bg);
  border-color: var(--priority-p1-idle-bg);
  outline: 2px solid var(--priority-p1-idle-ring);
  outline-offset: 1px;
}
.chip-p1.is-active {
  color: var(--priority-p1-text);
  background: var(--priority-p1-bg);
  border-color: var(--priority-p1-bg);
  outline: 0;
}
.chip-p2 {
  color: var(--priority-p2-text);
  background: var(--priority-p2-bg);
  border-color: var(--priority-p2-bg);
}
.chip-p3 {
  color: var(--priority-p3-text);
  background: var(--priority-p3-bg);
  border-color: var(--priority-p3-border);
}
.chip-p4 { color: var(--priority-p4-text); border-color: var(--priority-p4-border); }
.chip-p5 { color: var(--priority-p5-text); border-color: var(--priority-p5-border); }
.escalated { font-size: var(--fs-small); font-weight: var(--weight-strong); color: var(--text); }

/* ---- day strips, the day axis, the key ------------------------------------- */

/* Second channel for days: height. A good day is a low bar, a degraded day
   half height, an outage full height; maintenance is a full-height dashed
   outline and no data a thin line. */

svg.days { display: block; width: 100%; height: auto; }
.d-ok { fill: var(--status-ok); }
.d-degraded { fill: var(--status-degraded); }
.d-down { fill: var(--status-down); }
.d-none { fill: var(--status-unknown); }
.d-maint {
  fill: var(--status-maint-bg);
  stroke: var(--status-maint);
  stroke-width: 1.5;
  stroke-dasharray: 3 2;
  vector-effect: non-scaling-stroke;
}
.hit { fill: transparent; }
.hit:hover { fill: var(--text); fill-opacity: 0.08; }

/* The shared axis: one grid column per day, exactly like the rows. */
.day-axis {
  display: grid;
  grid-template-columns: repeat(var(--days), minmax(0, 1fr));
  align-items: end;
  height: 2rem;
}
.tick {
  display: flex;
  flex-direction: column;
  align-items: center;
  white-space: nowrap;
  color: var(--text-muted);
}
.tick::after { content: ""; width: 1px; height: 5px; margin-top: 2px; background: var(--text-muted); }
.tick .data { font-size: var(--fs-micro); line-height: 1.2; }
/* hour-call.js hides a date that would touch its neighbour or "Today"
   (a narrow axis, "Yesterday"); the tick mark stays. */
.tick .tick-crowded { visibility: hidden; }
.tick-today {
  align-items: flex-end;
  color: var(--text);
  font-size: var(--fs-small);
  font-weight: var(--weight-strong);
  line-height: 1.2;
}
.tick-today::after { align-self: center; background: var(--text); }

.day-key {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-4);
  margin: var(--space-3) 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-small);
}
.day-key li { display: inline-flex; align-items: center; gap: 0.5em; }
.key-glyph { width: 0.6rem; height: 1.3rem; }
/* In a margin heading the key is a right-aligned column, glyphs outside. */
.margin-head .day-key { flex-direction: column; flex-wrap: nowrap; align-items: flex-end; gap: 0.1rem; }
.margin-head .key-glyph { order: 2; }

@media (max-width: 46rem) {
  .margin-head .day-key { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-4); margin-top: var(--space-2); }
  .margin-head .key-glyph { order: 0; }
}

/* ---- service rows (Service_row) -------------------------------------------- */

.svc-group { --days: 30; --strip: 390px; }
.svc-group.days-90 { --days: 90; --strip: 450px; }
.svc-group + .svc-group { margin-top: var(--space-8); }
.svc-group-head, .svc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--strip) 13.5rem;
  gap: var(--space-2) var(--space-6);
}
.svc-group-head {
  align-items: end;
  padding-bottom: var(--space-1);
  border-bottom: var(--border-width-strong) solid var(--text);
}
.svc-group-name {
  margin: 0;
  padding-bottom: 0.2rem;
  font-size: var(--fs-small);
  font-weight: var(--weight-strong);
  color: var(--text-muted);
}
.group-pct { text-align: right; font-size: var(--fs-small); color: var(--text-muted); padding-bottom: 0.2rem; }
/* An unnamed group: the axis keeps its column. */
.svc-group-head.no-name > .day-axis { grid-column: 2; }
.svc-list { margin: 0; padding: 0; list-style: none; }
.svc {
  align-items: center;
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}
.svc-id { display: flex; flex-direction: column; align-items: flex-start; gap: 0.1rem; }
.svc-name { margin: 0; font-size: var(--fs-ui); font-weight: var(--weight-strong); line-height: 1.3; }
.svc-id .badge { font-size: var(--fs-small); }
.svc.s-ok .svc-id .badge { font-weight: var(--weight-regular); }
.svc-pct { display: flex; flex-direction: column; align-items: flex-end; margin: 0; text-align: right; line-height: 1.3; }
.svc-pct .pct { font-size: var(--fs-body); font-weight: var(--weight-medium); }
.svc-pct .pct-none { font-size: var(--fs-small); font-weight: var(--weight-regular); color: var(--text-muted); }
.pct-label { display: none; font-size: var(--fs-small); color: var(--text-muted); }
.svc-year { margin-top: 0.15rem; font-size: var(--fs-small); color: var(--text-muted); white-space: nowrap; }
.svc-year .data { color: var(--text); font-size: var(--fs-small); }
/* More about a row, inside it (the internal view's fold): under the rest,
   across every column. */
.svc-fold { grid-column: 1 / -1; min-width: 0; }

/* Medium: the rows stack their uptime beside the strip. */
@media (max-width: 68rem) {
  .svc-group-head, .svc { grid-template-columns: minmax(0, 1fr) auto; }
  .svc-group-name { grid-column: 1 / -1; }
  .day-axis, .svc-group-head.no-name > .day-axis { grid-column: 1; width: var(--strip); max-width: 100%; }
  .group-pct { grid-column: 2; }
  .svc-id { grid-column: 1 / -1; }
  .svc-days { grid-column: 1; width: var(--strip); max-width: 100%; }
  .svc-pct { grid-column: 2; }
}

@media (max-width: 46rem) {
  .svc-group-head { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .day-axis, .svc-days { width: auto; }
  .group-pct { display: none; }
  .svc { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  .svc-id { flex-direction: row; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 0 var(--space-3); }
  .svc-days { grid-column: 1; }
  .svc-pct { grid-column: 1; flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-3); }
  .pct-label { display: inline; }
  .svc-year { margin-top: 0; }
}

/* ---- the SLA year grid (Sla_grid) ------------------------------------------ */

svg.sla-grid { display: block; width: 100%; max-width: 48rem; height: auto; }
/* The grid keeps its drawn size, so its labels stay 12 px: a column too
   narrow for the year scrolls it sideways. */
svg.sla-grid { min-width: 45rem; }
:has(> svg.sla-grid) { overflow-x: auto; }
.sla-grid .data { fill: var(--text-muted); font-size: var(--fs-micro); }

/* ---- the status block (Incident_block) ------------------------------------- */

.lede { position: relative; margin-top: var(--space-10); }
/* The lit stretch of the post: status colour for the block's height. */
.lede:not(.s-ok)::before {
  content: "";
  position: absolute;
  left: calc(var(--pub-m) - 1.25px);
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--c);
}
.lede.s-maint::before {
  background: repeating-linear-gradient(to bottom, var(--c) 0 10px, transparent 10px 16px);
}

.lrow { align-items: start; }
.lrow + .lrow { margin-top: var(--space-6); }
.lm {
  grid-column: 1;
  grid-row: 1;
  padding-right: var(--pub-p);
  text-align: right;
  font-size: var(--fs-small);
  line-height: var(--lh-ui);
  color: var(--text-muted);
}
.lm p { margin: 0; }
.lm .data { font-size: var(--fs-small); color: var(--text); }
.lmain { grid-column: 2; grid-row: 1; padding-left: var(--pub-p); max-width: 46rem; }
/* Line the margin up with the first line it annotates. */
.lrow-head .lm { padding-top: 2.6rem; }
.lrow-also .lm { padding-top: 0.1rem; }

.lede-badge { margin: 0; font-size: var(--fs-body); }
.lede-title {
  margin: var(--space-3) 0 0;
  max-width: 22ch;
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-display);
  line-height: 1.08;
  letter-spacing: -0.02em;
}
.lede .voice { margin-top: var(--space-4); }
.dek { margin: var(--space-3) 0 0; font-size: var(--fs-lede); line-height: 1.4; }
.no-post { margin: var(--space-2) 0 0; color: var(--text-muted); }

.also { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.also li { display: flex; gap: var(--space-3); align-items: flex-start; }
.also li > .ico { color: var(--c); width: 1.25rem; height: 1.25rem; margin-top: 0.12rem; }
.also p { margin: 0; font-size: var(--fs-ui); line-height: var(--lh-ui); }
.also strong { font-weight: var(--weight-strong); }
.also-more { color: var(--text-muted); }

/* The latest update: set off by a hairline, not a box. The hairline runs
   the main column's whole width, as every other rule does (audit, public
   page 7); the words keep the block's measure. */
.lrow-update .lmain { max-width: none; padding-top: var(--space-5); border-top: 1px solid var(--border); }
.lrow-update .lmain > * { max-width: calc(46rem - var(--pub-p)); }
.lrow-update .lm { padding-top: calc(var(--space-5) + 0.1rem); }
.post-title { margin: 0; font-size: var(--fs-ui); font-weight: var(--weight-strong); line-height: var(--lh-ui); }
.post-affects { margin: var(--space-1) 0 0; font-size: var(--fs-small); color: var(--text-muted); }
.lrow-update .update-head { margin-top: var(--space-1); }
.inline-time { display: none; }
.inline-time .data { color: var(--text-muted); font-size: var(--fs-small); }
.lrow-update .update-body { margin-top: var(--space-2); }

/* Future maintenance: one quiet line. */
.upcoming-row { margin-top: var(--space-6); }
.upcoming > summary {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  padding-top: 0.6rem;
  font-size: var(--fs-small);
  color: var(--text-muted);
}
.upcoming > summary::before { margin-top: 0.55em; }
.upcoming > summary .ico { color: var(--ct); width: 1.125rem; height: 1.125rem; margin-top: 0.1rem; }
.up-word { color: var(--ct); font-weight: var(--weight-strong); }
.upcoming > summary .data { font-size: var(--fs-small); color: var(--text); }
.upcoming > summary:hover > span { text-decoration: underline; text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
.up-when { display: block; }
/* Under the words: past the disclosure marker (1.19em) and the icon. */
.up-body { margin: var(--space-1) 0 0 calc(1.19em + 1.125rem + var(--space-2)); font-size: var(--fs-small); }
.up-body p { margin: 0; }
.up-affects { color: var(--text-muted); }

@media (max-width: 46rem) {
  .lede { margin-top: var(--space-8); }
  .lm, .lrow-head .lm, .lrow-also .lm, .lrow-update .lm {
    grid-column: 2;
    grid-row: auto;
    padding: 0 0 0 var(--pub-p);
    text-align: left;
  }
  .lrow-head .lm { grid-row: 2; margin-top: var(--space-2); }
  .lm { display: flex; gap: 0.35em; }
  .lrow-also .lm, .lrow-update .lm { display: none; }
  .lrow-update .lmain { padding-top: var(--space-4); }
  .inline-time { display: inline; }
  .lrow + .lrow { margin-top: var(--space-5); }
  .lede-title { font-size: var(--fs-display-narrow); }
  .s-maint .lede-title { font-size: var(--fs-h2); }
  .dek { font-size: var(--fs-ui); }
  .also p { font-size: var(--fs-body); }
}

/* ---- timelines and recent incidents (Timeline) ----------------------------- */

.updates {
  max-width: 44rem;
  margin: 0;
  padding: 0;
  list-style: none;
  border-left: var(--border-width-strong) solid var(--border);
}
.update { position: relative; padding: 0 0 var(--space-5) var(--space-5); }
.update::before {
  content: "";
  position: absolute;
  left: -5.75px;
  top: 0.45rem;
  width: 10px;
  height: 10px;
  background: var(--ground);
  border: var(--border-width-strong) solid var(--text-muted);
}
.update-head { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-3); align-items: baseline; margin: 0; }
.update-head .data { color: var(--text-muted); font-size: var(--fs-small); }
.update-body { margin-top: var(--space-1); }
.update-body p { margin: 0; }
.update-body p + p { margin-top: var(--space-2); }

.earlier { margin-top: var(--space-2); }
.earlier > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  color: var(--link);
  font-size: var(--fs-small);
}
.earlier > summary:hover span { text-decoration: underline; text-underline-offset: 0.18em; }
.earlier .updates { margin: var(--space-2) 0 0 0.3rem; }

/* Recent incidents on a public page. */
.incidents { margin: 0; padding: 0; list-style: none; }
.incident + .incident { border-top: 1px solid var(--border); }
.inc-sum {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  gap: var(--space-1) var(--space-5);
  padding: var(--space-4) 0;
}
.incident:first-child .inc-sum { padding-top: 0; }
.inc-date { display: flex; flex-direction: column; gap: 0.1rem; padding-top: 0.3rem; font-size: var(--fs-small); }
.inc-date .data { font-size: var(--fs-small); }
.inc-date .data + .data { color: var(--text-muted); }
.inc-main { display: flex; flex-direction: column; gap: 0.2rem; }
.inc-title {
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
  line-height: 1.25;
}
.incident summary:hover .inc-title { text-decoration: underline; text-underline-offset: 0.18em; text-decoration-thickness: 1px; }
.inc-affects { font-size: var(--fs-small); color: var(--text-muted); }
.inc-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-5);
  margin-top: var(--space-1);
  font-size: var(--fs-small);
}
.inc-note .poststate { font-weight: var(--weight-regular); }
.inc-count { display: inline-flex; align-items: center; gap: 0.25em; color: var(--link); }
.incident .updates { margin: var(--space-1) 0 var(--space-6) calc(8.5rem + var(--space-5)); }
.none { margin: 0; color: var(--text-muted); }
/* An outage nobody posted about. */
.inc-unposted { color: var(--text-muted); }
.incident .inc-detail {
  max-width: 44rem;
  margin: 0 0 var(--space-2) calc(8.5rem + var(--space-5));
  font-size: var(--fs-small);
}
.incident .inc-detail:last-child { margin-bottom: var(--space-6); }
.inc-detail .data { font-size: var(--fs-small); }

@media (max-width: 46rem) {
  .inc-sum { grid-template-columns: minmax(0, 1fr); }
  .inc-date { flex-direction: row; gap: var(--space-3); padding-top: 0; }
  .incident .updates { margin-left: 0.25rem; }
  .incident .inc-detail { margin-left: 0.25rem; }
}

/* ---- polled regions (Region) ----------------------------------------------- */

/* A polled region is only a wrapper: what it holds lays out as before. */
.live { min-width: 0; }

/* Freshness (S1): "Updated 22:04 UTC", and the notice hour-call.js shows
   when polling stops working: ink, the problem icon, an indigo border like
   every other problem; never a status colour. */
.region-updated { margin: 0 0 var(--space-2); color: var(--text-muted); font-size: var(--fs-small); }
[data-stale] .region-updated { display: none; }
.region-stale-note {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  max-width: 40rem;
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-3);
  color: var(--text);
  background: var(--surface);
  border: 2px solid var(--field-error-border);
  border-radius: var(--radius);
}
.region-stale-note[hidden] { display: none; }
.region-stale-note > .ico { flex: none; align-self: flex-start; width: 1.25rem; height: 1.25rem; margin-top: 0.15em; }
.region-stale-text p { margin: 0; }
.region-stale-head { font-weight: var(--weight-strong); }
.region-stale-nodes { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-3); }
.region-stale-nodes ul { display: flex; flex-wrap: wrap; gap: 0 var(--space-1); margin: 0; padding: 0; list-style: none; }
.region-stale-nodes a { display: inline-flex; align-items: center; justify-content: center; min-height: var(--touch-min); min-width: var(--touch-min); }
/* The public status block's region sits outside the two columns. */
.pub main > .live > .region-status { padding-left: calc(var(--pub-m) + var(--pub-p)); }
.pub main > .live > .region-status .region-stale-note { margin: var(--space-8) 0 0; }
.pub main > .live[data-stale] > .lede { margin-top: var(--space-6); }
.pub .block-main > .region-status .region-stale-note { margin: 0 0 var(--space-4); }

/* Stable swaps (S12): a row that arrived since the page loaded carries an
   ink "New" label, added by hour-call.js; no colour and no motion. */
.row-new {
  display: inline-block;
  margin-right: var(--space-2);
  padding: 0 var(--space-1);
  font-size: var(--fs-small);
  font-weight: var(--weight-strong);
  line-height: var(--lh-ui);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}
.alert-prio > .row-new { display: table; margin: var(--space-2) 0 0; }

/* ---- alert rows (Alert_row) ------------------------------------------------ */

.alert-list { margin: 0; padding: 0; list-style: none; border-top: var(--border-width-strong) solid var(--text); }
/* Wide: chip, what is wrong, how it stands, since when; the state column
   fits "Acknowledged by Sam Okafor" and the since column a day and a time
   (audit alerts 2). Between 46rem and 60rem (tablets) the title keeps the
   width: since sits beside it and the state under it (alerts 1). */
.alert-row {
  display: grid;
  grid-template-columns: 5rem minmax(0, 1fr) 14rem 14rem;
  gap: var(--space-2) var(--space-5);
  align-items: start;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}
.alert-prio { padding-top: 0.7rem; }
.alert-prio .prio { flex-direction: column; align-items: flex-start; gap: 0.2rem; }
.alert-main { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
.alert-title {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  color: var(--text);
  font-size: var(--fs-ui);
  font-weight: var(--weight-strong);
  text-decoration: none;
}
.alert-title:hover { text-decoration: underline; text-underline-offset: 0.18em; }
.alert-where { color: var(--text-muted); font-size: var(--fs-small); }
.alert-facts { margin: var(--space-1) 0 0; padding: 0; list-style: none; font-size: var(--fs-small); }
.alert-state { display: flex; flex-direction: column; gap: 0.2rem; padding-top: 0.7rem; font-size: var(--fs-small); }
.alert-state .incstate { font-weight: var(--weight-regular); }
.alert-since { margin: 0; padding-top: 0.7rem; text-align: right; color: var(--text-muted); font-size: var(--fs-small); }
.alert-since .data { font-size: var(--fs-small); color: var(--text); }
/* A time with its day ("Wed 23 Sep 20:31 UTC") stays on one line. */
.alert-since time { white-space: nowrap; }

.alert-acts { grid-column: 2; display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-1); }
.alert-act { margin: 0; }

@media (max-width: 60rem) {
  .alert-row { grid-template-columns: 5rem minmax(0, 1fr) auto; }
  .alert-prio { grid-row: 1 / span 3; }
  .alert-main { grid-column: 2; grid-row: 1; }
  .alert-since { grid-column: 3; grid-row: 1; }
  .alert-state { grid-column: 2 / -1; grid-row: 2; flex-direction: row; flex-wrap: wrap; gap: var(--space-1) var(--space-4); padding-top: 0; }
  .alert-acts { grid-column: 2 / -1; grid-row: 3; }
}
@media (max-width: 46rem) {
  .alert-row { grid-template-columns: auto minmax(0, 1fr); gap: 0 var(--space-3); padding: var(--space-2) 0 var(--space-3); }
  .alert-main, .alert-state, .alert-since, .alert-acts { grid-column: 2; grid-row: auto; }
  .alert-prio { grid-row: 1 / span 3; }
  .alert-state, .alert-since { grid-column: 2; padding-top: var(--space-1); }
  .alert-state { flex-direction: row; flex-wrap: wrap; gap: var(--space-1) var(--space-4); }
  .alert-since { text-align: left; }
}

/* ---- the alert heading (Alert_head) ---------------------------------------- */

.alert-head {
  display: grid;
  grid-template-columns: 5rem minmax(0, 1fr);
  gap: var(--space-2) var(--space-5);
  margin: var(--space-2) 0 var(--space-5);
  padding: var(--space-4) 0;
  border-top: var(--border-width-strong) solid var(--text);
  border-bottom: 1px solid var(--border);
}
.alert-head-prio { padding-top: 0.45rem; }
.op-main .alert-head-title { margin: 0; font-size: var(--fs-h2); overflow-wrap: anywhere; }
.alert-head-where { margin: var(--space-1) 0 0; color: var(--text-muted); overflow-wrap: anywhere; }
.alert-head-status {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-5);
  margin: var(--space-3) 0 0;
}
.alert-head-since { color: var(--text-muted); }
.alert-head-since .data { color: var(--text); }
.alert-head-facts { margin: var(--space-2) 0 0; padding: 0; list-style: none; }
.alert-head-facts li { margin: var(--space-1) 0 0; }
.alert-head-facts li:first-child { font-weight: var(--weight-strong); }

@media (max-width: 46rem) {
  .alert-head { grid-template-columns: minmax(0, 1fr); padding-top: var(--space-3); }
  .alert-head-prio { padding-top: 0; }
  .op-main .alert-head-title { font-size: var(--fs-h3); }
}

/* ---- what each location reports (Vantages) --------------------------------- */

.vantages { margin: 0 0 var(--space-6); }
.op-main .vt-check { margin: 0 0 var(--space-1); }
.vt-summary { margin: 0; font-weight: var(--weight-strong); }
.vt-note-line { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--fs-small); }
.vt-facts { margin: var(--space-1) 0 0; padding: 0; list-style: none; }
.vt-locations { margin: var(--space-3) 0 0; padding: 0; list-style: none; border-top: 1px solid var(--border); }
.vt-location { border-bottom: 1px solid var(--border); }
.vt-location-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-3); margin: var(--space-2) 0 0; font-size: var(--fs-small); }
.vt-location-name { color: var(--text-muted); }
.vt-nodes { margin: 0; padding: 0; list-style: none; }
.vt-node {
  display: grid;
  grid-template-columns: 5rem 9rem minmax(0, 1fr);
  gap: var(--space-1) var(--space-4);
  align-items: center;
  padding: var(--space-2) 0 var(--space-3);
}
.vt-node-status .badge { font-size: var(--fs-small); }
svg.vt-strip { display: block; width: 100%; height: 1.75rem; }
.vt-onset { stroke: var(--text); stroke-width: 1.5; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.vt-node-facts {
  grid-column: 2 / -1;
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-4);
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.vt-node-facts .data { font-size: var(--fs-small); color: var(--text); }
.vt-detail, .vt-note { color: var(--text); }
.vt-axis {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 var(--space-4);
  margin: var(--space-1) 0 0 calc(14rem + 2 * var(--space-4));
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.vt-axis .data { font-size: var(--fs-micro); }

@media (max-width: 46rem) {
  .vt-check a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
  .vt-node { grid-template-columns: minmax(0, 1fr) auto; }
  .vt-node svg.vt-strip, .vt-node-facts { grid-column: 1 / -1; }
  .vt-axis { margin-left: 0; }
}

/* ---- the notification log (Event_log) -------------------------------------- */

/* Time, icon, sentence; details under it. A failed send is a fact: ink,
   the problem icon, a heavier word; never red. */
.evlog-summary { margin: 0 0 var(--space-3); max-width: var(--measure); }
.evlog { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--border); }
/* The time column fits the longest time it prints ("01:48 UTC", or a
   local "01:48 GMT+10"), no wider (re-audit N19); a longer zone name widens
   its own row rather than running under the icon. */
.ev {
  display: grid;
  grid-template-columns: minmax(7rem, max-content) 1.25rem minmax(0, 1fr);
  gap: 0 var(--space-3);
  align-items: baseline;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border);
}
/* A log that is not all today prints the day on every row: a wider time. */
.evlog-days .ev { grid-template-columns: minmax(13.5rem, max-content) 1.25rem minmax(0, 1fr); }
.ev-at { color: var(--text-muted); white-space: nowrap; }
.ev-ico { align-self: center; color: var(--text-muted); }
.ev-text { margin: 0; }
.ev-detail {
  grid-column: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-4);
  margin: var(--space-1) 0 0;
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.ev-detail .data { font-size: var(--fs-small); }
.ev-failed .ev-ico, .ev-escalated .ev-ico, .ev-acked .ev-ico { color: var(--text); }
.ev-failed .ev-text, .ev-escalated .ev-text, .ev-handoff .ev-text, .ev-acked .ev-text { font-weight: var(--weight-strong); }

/* The time goes over the sentence when the log is narrow: on a phone, and
   in the alert detail's main column on a laptop when every row carries its
   day (a container query on the log's own width). */
.evlog { container: evlog / inline-size; }
@container evlog (max-width: 30rem) {
  .ev { grid-template-columns: 1.25rem minmax(0, 1fr); }
  .ev-at { grid-column: 2; grid-row: 1; }
  .ev-ico { grid-column: 1; grid-row: 2; }
  .ev-text { grid-column: 2; grid-row: 2; }
  .ev-detail { grid-column: 2; }
}
@container evlog (max-width: 40rem) {
  .evlog-days .ev { grid-template-columns: 1.25rem minmax(0, 1fr); }
  .evlog-days .ev-at { grid-column: 2; grid-row: 1; }
  .evlog-days .ev-ico { grid-column: 1; grid-row: 2; }
  .evlog-days .ev-text { grid-column: 2; grid-row: 2; }
  .evlog-days .ev-detail { grid-column: 2; }
}
@media (max-width: 46rem) {
  .ev, .evlog-days .ev { grid-template-columns: 1.25rem minmax(0, 1fr); }
  .ev-at { grid-column: 2; grid-row: 1; }
  .ev-ico { grid-column: 1; grid-row: 2; }
  .ev-text { grid-column: 2; grid-row: 2; }
  .ev-detail { grid-column: 2; }
}

/* ---- check rows (Check_row) ------------------------------------------------ */

/* "Flapping" is ink, never a status colour. The rows reflow by the width
   of their group (a container query), not the window's, so they also fit a
   narrow column. */
.check-group { margin-top: var(--space-6); }
.op-main .check-group-name { margin: 0; font-size: var(--fs-h3); overflow-wrap: anywhere; }
.check-group-sub { margin: 0.1rem 0 var(--space-2); color: var(--text-muted); font-size: var(--fs-small); }
.check-list { margin: 0; padding: 0; list-style: none; }
/* Rows are links: they light up under the pointer. The rows and their
   rules sit on the content edge, in line with the heading; the hover band
   reaches a step into the gutter on each side (as the kit's tables do). */
.check-cols, .check-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 9.5rem 13rem 11rem 10rem;
  gap: 0 var(--space-4);
}
.check-cols {
  align-items: end;
  padding-bottom: var(--space-1);
  border-bottom: var(--border-width-strong) solid var(--text);
  color: var(--text-muted);
  font-size: var(--fs-small);
}
/* Two uptime columns, each under its own label (audit, checks 5). */
.col-up { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 var(--space-3); text-align: right; }
.col-up-name { grid-column: 1 / -1; }
.check-row {
  align-items: baseline;
  padding-block: 0 var(--space-1);
  border-bottom: 1px solid var(--border);
}
/* A row answers the pointer (audit, checks 3; one tone, re-audit N2): the
   kit table's row hover, --surface, carried a step past the text by a
   shadow on each side, and the name underlined. */
.check-row:hover { background: var(--surface); box-shadow: calc(-1 * var(--space-2)) 0 0 var(--surface), var(--space-2) 0 0 var(--surface); }
.check-row:hover .check-name, .check-name:hover { text-decoration: underline; text-underline-offset: 0.18em; }
.check-row.s-ok .badge { font-weight: var(--weight-regular); }
/* One baseline across the row (audit, checks 4): a badge's and a
   location's words, not their icons, set where they sit. */
.check-row :is(.badge, .loc) { align-items: baseline; }
.check-row :is(.badge, .loc) > .ico { align-self: center; }
.check-id { min-width: 0; }
.check-name {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  min-height: var(--touch-min);
  color: var(--text);
  font-size: var(--fs-body);
  text-decoration: none;
  overflow-wrap: anywhere;
}
/* The name's pieces in one flex item, so they wrap as one line of text
   (re-audit N1). */
.check-name-text { min-width: 0; }
.check-kind { margin: 0; color: var(--text-muted); font-size: var(--fs-small); }
.check-kind .data { font-size: var(--fs-small); color: var(--text-muted); white-space: nowrap; }
.check-state { font-size: var(--fs-small); }
.check-status { display: flex; flex-wrap: wrap; align-items: center; gap: 0.1rem var(--space-3); }
.flap { color: var(--text); font-weight: var(--weight-strong); }
.check-facts { margin: 0.1rem 0 0; padding: 0; list-style: none; color: var(--text-muted); }
.check-where { font-size: var(--fs-small); }
.locs { display: flex; flex-wrap: wrap; gap: 0.2rem var(--space-3); margin: 0; padding: 0; list-style: none; }
.loc { display: inline-flex; align-items: center; gap: 0.3em; color: var(--ct); }
.loc .data { font-size: var(--fs-small); color: var(--text); }
.check-last { margin: 0; color: var(--text-muted); }
.check-last .data { font-size: var(--fs-small); color: var(--text); white-space: nowrap; }
.check-uptime { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 var(--space-3); margin: 0; font-size: var(--fs-small); text-align: right; }
.check-uptime .data { font-size: var(--fs-small); }
.up { white-space: nowrap; }
.up-full .data, .up-none { color: var(--text-muted); }
.up-none { grid-column: 1 / -1; }
.check-uptime .up-full .data, .up-none { color: var(--text-muted); }
.up-label { color: var(--text-muted); }
.check-group { container: checks / inline-size; }
@container checks (min-width: 64.01rem) {
  /* The column labels say it; the words stay for screen readers. */
  .check-row .up-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}
@container checks (max-width: 64rem) {
  .check-cols { display: none; }
  .check-list { border-top: var(--border-width-strong) solid var(--text); }
  .check-row { grid-template-columns: minmax(0, 1fr) 13rem 9rem; padding-bottom: var(--space-2); }
  .check-id { grid-column: 1; }
  .check-kind { grid-column: 1; grid-row: 2; }
  .check-state { grid-column: 2; grid-row: 1 / span 2; padding-top: 0.7rem; }
  .check-where { grid-column: 1; grid-row: 3; padding-top: var(--space-1); }
  .check-uptime { grid-column: 3; grid-row: 1 / span 2; grid-template-columns: 1fr; gap: 0; padding-top: 0.7rem; }
  .up-none { grid-column: auto; }
  .check-uptime .up { width: auto; }
}
@container checks (max-width: 40rem) {
  .check-row { grid-template-columns: minmax(0, 1fr) auto; gap: 0.15rem var(--space-3); }
  .check-id { grid-column: 1 / -1; }
  .check-state { grid-column: 1; grid-row: 2; padding-top: 0; }
  .check-uptime { grid-column: 2; grid-row: 2 / span 2; padding-top: 0; }
  .check-where { grid-column: 1; grid-row: 3; padding-top: 0; }
  .check-kind { grid-column: 1 / -1; grid-row: 4; }
}

/* ---- node rows (Node_row) -------------------------------------------------- */

/* The same fact column in every row, so the rows read as a table. */
.node-list { margin: 0; padding: 0; list-style: none; border-top: var(--border-width-strong) solid var(--text); }
.node-row {
  display: grid;
  grid-template-columns: minmax(12rem, 1fr) 10rem minmax(0, 2fr);
  gap: var(--space-2) var(--space-5);
  align-items: baseline;
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border);
}
.node-id { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-3); }
.node-id .ico { align-self: center; color: var(--text-muted); }
.node-name { font-size: var(--fs-ui); color: var(--text); }
.node-self { font-size: var(--fs-small); font-weight: var(--weight-strong); }
.node-zone { font-size: var(--fs-small); color: var(--text-muted); }
.node-facts { display: grid; grid-template-columns: 7rem minmax(0, 1fr); gap: 0 var(--space-3); margin: 0; font-size: var(--fs-small); }
.node-facts dt { color: var(--text-muted); }
.node-facts dd { margin: 0; }
.node-facts .data { font-size: var(--fs-small); }
.node-note { grid-column: 3; margin: 0; font-size: var(--fs-small); color: var(--text-muted); }

@media (max-width: 46rem) {
  .node-row { grid-template-columns: minmax(0, 1fr) auto; }
  .node-facts, .node-note { grid-column: 1 / -1; }
}

/* ---- escalation chains (Chain) --------------------------------------------- */

/* The name in the margin, then one sentence, the steps with their delays,
   and the services that use it. Read-only; ink only. */
.chains { margin: 0; }
.chain {
  display: grid;
  grid-template-columns: 12rem minmax(0, 1fr);
  gap: var(--space-2) var(--space-6);
  padding: var(--space-5) 0;
  border-bottom: var(--border-width) solid var(--border);
}
.chain-head { min-width: 0; }
.chain .chain-name { margin: 0; font-size: var(--fs-ui); font-weight: var(--weight-medium); overflow-wrap: anywhere; }
/* A name in words (the operator chain) is a heading in the UI font: its
   semibold, not Plex Mono's medium (re-audit N16). */
.chain .chain-name:not(.data) { font-weight: var(--weight-strong); }
.chain-origin { margin: var(--space-1) 0 0; color: var(--text-muted); font-size: var(--fs-small); }
.chain-origin .data { font-size: var(--fs-small); }
.chain-sentence { margin: 0 0 var(--space-3); font-size: var(--fs-ui); line-height: var(--lh-ui); color: var(--text); }
.chain-steps { margin: 0; padding: 0; list-style: none; }
.chain-step {
  display: grid;
  grid-template-columns: 5rem minmax(0, 1fr);
  gap: 0 var(--space-3);
  padding: var(--space-2) 0;
  border-top: var(--border-width) solid var(--border);
}
.chain-at { color: var(--text-muted); }
.chain-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--space-1) var(--space-3); min-width: 0; }
.chain-target { font-weight: var(--weight-strong); }
.chain-kind { color: var(--text-muted); font-size: var(--fs-small); }
.chain-kind .data { font-size: var(--fs-small); }
.chain-skip {
  padding: 0 var(--space-1);
  border: var(--border-width) solid var(--field-error-border);
  border-radius: var(--radius);
  color: var(--text);
  font-size: var(--fs-small);
}
.chain-members {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-small);
}
.chain-members li { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-2); }
.chain-used { margin: var(--space-3) 0 0; color: var(--text-muted); font-size: var(--fs-small); }

@media (max-width: 46rem) {
  .chain { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  .chain-step { grid-template-columns: 3.5rem minmax(0, 1fr); }
}

/* ---- buttons (Button) ------------------------------------------------------ */

/* One look per level (audit S7). Primary: the indigo plane, one per view.
   Secondary: an outline in paper text. Quiet: link-coloured words. The
   destructive button ends a confirmation step: a filled ink plane, never
   red (brief rules 1 and 6). 44 px everywhere; compact (36 px) only in
   dense rows on a wide screen. Hover fills with --raised. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  margin: 0;
  padding: 0 var(--space-4);
  color: var(--text);
  background: transparent;
  font: inherit;
  font-size: var(--fs-body);
  font-weight: var(--weight-strong);
  line-height: var(--lh-tight);
  text-align: center;
  text-decoration: none;
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius);
  cursor: pointer;
}
.btn:hover { color: var(--text); background: var(--raised); border-color: var(--text); }
.btn .ico { flex: none; width: 1.125rem; height: 1.125rem; }
.btn-primary { color: var(--text-on-plane); background: var(--plane); border: var(--border-width-strong) solid var(--link); }
.btn-primary:hover { color: var(--ground); background: var(--link); border-color: var(--link); }
.btn-quiet {
  padding: 0 var(--space-2);
  color: var(--link);
  font-weight: var(--weight-regular);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  border-color: transparent;
}
.btn-quiet:hover { color: var(--text); border-color: transparent; }
/* The committed action of a confirmation step: a filled ink plane, so it
   never reads like the "Keep" beside it (re-audit N4). */
.btn-destructive { color: var(--ground); background: var(--text); border: var(--border-width-strong) solid var(--text); }
.btn-destructive:hover { color: var(--ground); background: var(--text); border-color: var(--text); text-decoration: underline; text-underline-offset: 0.18em; }
.btn-compact { min-height: 2.25rem; padding: 0 var(--space-3); font-size: var(--fs-small); }
/* A row of buttons, the primary first. */
.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); margin: 0; }

@media (max-width: 46rem) {
  .btn-compact { min-height: var(--touch-min); padding: 0 var(--space-4); font-size: var(--fs-body); }
}

/* ---- notices after a change (Layout.Notice) -------------------------------- */

/* What a change did, naming what it was about (audit S5): ink on the
   surface with an ink rule, never a status colour, in the flow at the
   content's full width (re-audit N3). land.js focuses it and scrolls the
   thing it names into view; then it pins the notice to the top of the
   window (is-pinned), so the sentence and the row are both in sight, until
   the viewer scrolls on. Pinned, it is a bar: the content's width, with a
   band of the ground below and beside it, so nothing it passes over peeks
   out around it. The thing itself is marked (is-landed, or :target without
   script). */
.flash.notice {
  margin: 0 0 var(--space-4);
  padding: var(--space-2) var(--space-4);
  color: var(--text);
  background: var(--surface);
  border-left: var(--border-width-strong) solid var(--text);
}
.flash.notice.is-pinned { position: sticky; top: 0; z-index: 3; }
.is-landed, .alert-row:target, .ou-row:target { background: var(--surface); box-shadow: inset 3px 0 0 var(--text); }
.is-landed, :target { scroll-margin-top: 4.5rem; }
.refusal[tabindex] { scroll-margin-top: var(--space-4); }
/* Focused by script to be read out, not to be typed in: no ring. */
.refusal[tabindex="-1"]:focus, .flash.notice:focus { outline: none; box-shadow: none; }
.flash.notice.is-pinned, .flash.notice.is-pinned:focus { box-shadow: 0 var(--space-2) 0 var(--space-2) var(--ground); }

/* ---- forms (Field, Confirm) ------------------------------------------------ */

/* Fields: a label, a hint, the control, and a refusal in bold ink with the
   problem icon while the control's border turns indigo (brief rule 6: never
   red). Controls and choices are 44 px targets. */

.field { display: grid; gap: var(--space-1); margin: 0 0 var(--space-4); max-width: 32rem; }
.field > label:not(.choice) { color: var(--text); font-weight: var(--weight-strong); font-size: var(--fs-body); }
.field-hint { margin: 0; color: var(--text-muted); font-size: var(--fs-small); }
.field input:not([type="checkbox"]):not([type="radio"]), .field select, .field textarea {
  display: block;
  width: 100%;
  max-width: none;
  min-height: var(--touch-min);
  margin: 0;
  padding: var(--space-2) var(--space-3);
  color: var(--text);
  background: var(--ground);
  border: var(--border-width) solid var(--border-strong);
  border-radius: var(--radius);
}
.field.is-invalid input:not([type="checkbox"]):not([type="radio"]),
.field.is-invalid select,
.field.is-invalid textarea { border: 2px solid var(--field-error-border); }
.field-check { display: flex; }
.choices.is-invalid {
  max-width: 40rem;
  padding: var(--space-1) var(--space-4) var(--space-3);
  border: 2px solid var(--field-error-border);
  border-radius: var(--radius);
}
.choices.is-invalid > legend { padding: 0 var(--space-2); margin-left: calc(-1 * var(--space-2)); }
.field-error {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0;
  color: var(--text);
  font-size: var(--fs-small);
  font-weight: var(--weight-strong);
}
.field-error .ico { align-self: center; }

.choices { margin: 0 0 var(--space-5); padding: 0; border: 0; min-width: 0; }
.choices > legend { padding: 0; margin-bottom: var(--space-1); font-weight: var(--weight-strong); color: var(--text); }
.choices > .field-hint { margin-bottom: var(--space-2); max-width: var(--measure); }
.choice {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  margin-right: var(--space-5);
  color: var(--text);
  font-size: var(--fs-body);
  cursor: pointer;
}
.choice input { width: 1.125rem; height: 1.125rem; margin: 0; accent-color: var(--link); }
.choice-note { color: var(--text-muted); font-size: var(--fs-small); }
.choices > .choice { display: flex; margin-right: 0; }

/* A filter form: Fields in a row, their 44 px controls level at the bottom
   whatever their hints, the button beside them; one column on a phone. */
.filters .field { flex: 1 1 11rem; min-width: 0; max-width: 18rem; margin: 0; }
.filters .field-check { flex-basis: 100%; max-width: none; }
.filters > .btn-row { flex: 0 0 auto; }
/* Every filter's label on one line (re-audit, archive 5): a field without a
   hint keeps a hint's line under its label, so the labels line up as the
   controls do. */
.filters .field-hint { line-height: var(--lh-ui); }
.filters .field:not(.field-check):not(:has(> .field-hint)) > label {
  margin-bottom: calc(var(--fs-small) * var(--lh-ui) + var(--space-1));
}
@media (max-width: 46rem) {
  .filters .field { flex-basis: 100%; max-width: none; }
}

/* The confirmation step for destructive actions: a details element, no
   colour (brief rule 6), one shape on every page (re-audit N4). It is the
   last item of a row that wraps, or on a line of its own. The summary is a
   button, so it has no disclosure chevron (N8), and it never moves: open,
   the details lets its children into the row (display: contents), the
   summary keeping the step's place and the body taking a line of its own
   across the row, in the flow under it. "Keep" (shown by land.js) comes
   first, then the destructive button, side by side. */
.confirm { display: inline-block; }
.confirm[open] { display: contents; }
.confirm > summary.btn::before { content: none; }
.confirm[open] > .confirm-open { margin-left: auto; border-color: var(--text); background: var(--raised); }
.confirm-body {
  display: flex;
  flex: 1 0 100%;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  box-sizing: border-box;
  width: 100%;
  margin-top: var(--space-2);
  padding: var(--space-3) var(--space-4) var(--space-4);
  text-align: left;
  background: var(--surface);
  border-left: var(--border-width-strong) solid var(--text);
}
/* The question has the body's first line to itself, its words kept to a
   readable measure; the buttons follow on the next. */
.confirm-question { flex: 0 0 100%; box-sizing: border-box; padding-right: max(0px, 100% - var(--measure)); margin: 0 0 var(--space-1); color: var(--text); font-size: var(--fs-small); line-height: var(--lh-ui); }
.confirm-body form { margin: 0; }

/* ---- problems (Problem) ---------------------------------------------------- */

/* Something to fix, never a status: ink text, the problem icon, an indigo
   border. A list of problems (Problem.list, Problem.section) and a form-level
   refusal (Problem.refusal) are the same box. */

/* A list of problems is one quiet box (audit, policies 1): the surface,
   the indigo edge down its side, hairlines between the items; a refusal
   keeps the full border, since it answers the viewer's own change. */
.problems {
  max-width: 40rem;
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
  background: var(--surface);
  border-left: 2px solid var(--field-error-border);
  border-radius: 0 var(--radius) var(--radius) 0;
}
.problem, .refusal {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  max-width: 40rem;
  padding: var(--space-1) var(--space-3);
  color: var(--text);
}
.refusal {
  background: var(--surface);
  border: 2px solid var(--field-error-border);
  border-radius: var(--radius);
}
.problem { margin: 0; padding-block: var(--space-2); font-size: var(--fs-small); }
.problem + .problem { border-top: var(--border-width) solid var(--border); }
.problem > .ico, .refusal > .ico { flex: none; align-self: flex-start; width: 1.25rem; height: 1.25rem; margin-top: 0.15em; }
.problem-text { margin: 0; }
.problem-subject { font-size: var(--fs-small); }
a.problem-subject { display: inline-block; }
@media (max-width: 46rem) {
  a.problem-subject { display: inline-flex; align-items: center; min-height: var(--touch-min); }
}
.problem-section { max-width: var(--measure); margin: var(--space-2) 0 0; }
.op-main .problem-section h2 { margin-top: 0; }

.refusal { margin: 0 0 var(--space-5); padding: var(--space-2) var(--space-3); }
.refusal p { margin: 0; }
.refusal ul { margin: var(--space-1) 0 0; padding-left: var(--space-5); }
.refusal-head { font-weight: var(--weight-strong); }
.refusal .refusal-fix { margin-top: var(--space-2); }
.refusal-fix a { display: inline-flex; align-items: center; min-height: var(--touch-min); }

/* ---- empty states: voice and a geometric illustration (Empty_state) -------- */

.empty {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
  max-width: 40rem;
  padding: var(--space-8) 0;
}
.empty .art { width: 10rem; height: 7.5rem; margin-bottom: var(--space-3); }
.empty-head {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
}
.empty-more { margin: 0; color: var(--text-muted); }
/* The panot texture (brief section 5): four interlocking rings per tile, as
   a faint background, only on operator all-clear states. It spans the
   content width and fades out towards every edge (no cut tiles). The art
   and the words keep clear ground: the texture rises only to their right,
   so nothing cuts a hole in it (audit alerts 7; re-audit: no plate). */
.empty-texture {
  position: relative;
  isolation: isolate;
  max-width: none;
  padding-left: var(--space-6);
  padding-right: var(--space-6);
}
.empty-texture::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    radial-gradient(circle at 50% 32%, transparent 6px, var(--border) 6px 7px, transparent 7px),
    radial-gradient(circle at 68% 50%, transparent 6px, var(--border) 6px 7px, transparent 7px),
    radial-gradient(circle at 50% 68%, transparent 6px, var(--border) 6px 7px, transparent 7px),
    radial-gradient(circle at 32% 50%, transparent 6px, var(--border) 6px 7px, transparent 7px);
  background-size: 40px 40px;
  background-position: center;
  -webkit-mask-image:
    linear-gradient(to right, transparent 26rem, #000 38rem, #000 calc(100% - 8rem), transparent),
    linear-gradient(to bottom, transparent, #000 30%, #000 70%, transparent);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent 26rem, #000 38rem, #000 calc(100% - 8rem), transparent),
    linear-gradient(to bottom, transparent, #000 30%, #000 70%, transparent);
  mask-composite: intersect;
}
.empty-plate {
  display: grid;
  justify-items: start;
  gap: var(--space-2);
  max-width: 40rem;
}
.art-plane { fill: var(--plane); }
.art-shadow { fill: none; stroke: var(--plane); stroke-width: 1.5; }
.art-lantern { fill: none; stroke: var(--brand-on-plane); stroke-width: 1.75; stroke-linejoin: miter; }
.art-lantern-pane { fill: var(--brand-on-plane); stroke: none; }
.art-lantern-ring { fill: none; stroke: var(--brand-on-plane); stroke-width: 1; stroke-dasharray: 2 4; }
.art-tile rect, .art-tile circle { fill: none; stroke: var(--plane); stroke-width: 1.5; }
.art-lantern-light { fill: var(--brand); }

/* ---- explanations: "How this works" (Explain) ------------------------------ */

/* A page's explanation, folded under its heading, in plain words. */
.how { max-width: var(--measure); margin: 0 0 var(--space-4); }
.how > summary { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--link); }
.how > summary .ico-chev { color: var(--text-muted); }
.how p { margin: var(--space-2) 0 0; color: var(--text-muted); }

/* ---- tables (Table) -------------------------------------------------------- */

/* Every data table in the workspace (UT, audit S9). The box scrolls
   sideways when the table is wider than the page, and down when it is
   taller than the window, with the header row stuck to its top: a table
   shorter than a screen never scrolls inside, and a longer one keeps its
   header in view without it ever covering the page's own. The box reaches a
   step into the gutter on each side and pads it back, so the table, its
   rules and its cells' text sit on the content edge, in line with the
   headings (re-audit N12), while a hovered row's band has room around it
   in the box's padding. Rules as the
   kit's lists draw them: a strong rule under the header, thin dividers
   between rows. Numbers are right-aligned, header included, in the data
   font with tabular figures, and never wrap away from their units. */
.tbl-box {
  overflow: auto;
  max-height: calc(100vh - var(--space-8));
  max-height: calc(100svh - var(--space-8));
  margin: var(--space-1) calc(-1 * var(--space-2)) var(--space-4);
  padding-inline: var(--space-2);
}
.tbl {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-small);
}
.tbl-fit { width: auto; min-width: min(100%, 36rem); }
.tbl :is(th, td) {
  padding: var(--space-2);
  text-align: left;
  vertical-align: top;
  border-bottom: var(--border-width) solid var(--border);
}
.tbl :is(th, td):first-child { padding-left: 0; }
.tbl :is(th, td):last-child { padding-right: 0; }
.tbl thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  vertical-align: bottom;
  background: var(--ground);
  color: var(--text-muted);
  font-weight: var(--weight-strong);
  border-bottom: var(--border-width-strong) solid var(--text);
}
.tbl tbody th { font-weight: var(--weight-regular); }
.tbl .num { text-align: right; white-space: nowrap; }
.tbl td.num { font-family: var(--font-data); font-size: var(--fs-data); font-variant-numeric: tabular-nums; word-spacing: -0.2em; }
.tbl time { white-space: nowrap; }
.tbl-words, .tbl-note { font-family: var(--font-ui); font-size: var(--fs-small); color: var(--text-muted); word-spacing: normal; }
.tbl-note { display: block; }
.tbl tfoot :is(th, td) { color: var(--text-muted); border-bottom: 0; }
/* A row that is a link lights up under the pointer, and its link says so. */
.tbl-linked tbody tr:hover > :is(td, th) { background: var(--surface); }
.tbl-linked tbody tr:hover > :first-child { box-shadow: calc(-1 * var(--space-2)) 0 0 var(--surface); }
.tbl-linked tbody tr:hover > :last-child { box-shadow: var(--space-2) 0 0 var(--surface); }
.tbl-linked tbody tr:hover > :only-child { box-shadow: calc(-1 * var(--space-2)) 0 0 var(--surface), var(--space-2) 0 0 var(--surface); }
.tbl-linked tbody tr:hover a { text-decoration: underline; text-decoration-color: currentColor; }
/* Sorting: the header is a link; the sorted one names its direction with
   a chevron (aria-sort says it to screen readers). */
.tbl-sort {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--border-strong);
  text-underline-offset: 0.18em;
}
.tbl-sort:hover { color: var(--text); text-decoration-color: currentColor; }
th[aria-sort] .tbl-sort { color: var(--text); }
.sort-mark {
  flex: none;
  width: 0.42em;
  height: 0.42em;
  border: solid currentColor;
  border-width: 0 0.1em 0.1em 0;
}
.sort-up { transform: translateY(0.12em) rotate(-135deg); }
.sort-down { transform: translateY(-0.12em) rotate(45deg); }

/* Phones: a stacked table is one block per row, each cell after its
   column's name; the header row stays for screen readers only. */
@media (max-width: 46rem) {
  .tbl-box:has(> .tbl-stack) { overflow: visible; max-height: none; margin-inline: 0; padding-inline: 0; }
  .tbl.tbl-stack { display: block; min-width: 0; }
  .tbl-stack thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  .tbl-stack tbody { display: block; border-top: var(--border-width-strong) solid var(--text); }
  .tbl-stack tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-1) var(--space-4);
    padding: var(--space-3) 0;
    border-bottom: var(--border-width) solid var(--border);
  }
  .tbl-stack td { display: block; padding: 0; border: 0; text-align: left; }
  .tbl-stack .tbl-note { display: inline; margin-left: 0.35em; }
  .tbl-stack td[data-label]::before {
    content: attr(data-label);
    margin-right: 0.4em;
    font-family: var(--font-ui);
    font-size: var(--fs-small);
    color: var(--text-muted);
  }
}

/* ---- in-page links (Jump) -------------------------------------------------- */

/* A long page's sections, one click away wherever the reader is: the row
   sticks to the top of the window on the ground colour, above a hairline.
   On a phone it scrolls sideways rather than wrapping into rows. */
.op-main { --jump-clear: 3.5rem; }
.jump {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 0 var(--space-4);
  background: var(--ground);
  border-bottom: var(--border-width) solid var(--border);
}
.jump ul { display: flex; flex-wrap: wrap; gap: 0 var(--space-5); margin: 0; padding: 0; list-style: none; }
.jump a { display: inline-flex; align-items: baseline; gap: 0.4em; min-height: var(--touch-min); padding-top: 0.7rem; }
.jump .data { font-size: var(--fs-small); }
.jump .jump-count { color: var(--text-muted); font-size: var(--fs-small); text-decoration: none; }
.jump-target { scroll-margin-top: var(--jump-clear); }
@media (max-width: 46rem) {
  .jump ul { flex-wrap: nowrap; overflow-x: auto; }
  .jump li { flex: none; }
}

/* ==== Screens =============================================================== */

/* ---- sign in (Sign_in, screen 6) ------------------------------------------- */

/* Medium personality, through type and colour only: the lit lantern and
   "Sereno" in Fraunces, D2's lamp post running down beside the form, and the
   indigo plane as the button. One column, the same on a phone: the header
   and the main share the body's middle grid column, so the post lines up
   under the lantern. */

/* Centred in the window, nudged above the middle (audit, sign in 1): the
   bottom padding outweighs the top by about 8vh. */
body[data-page="signin"] {
  display: grid;
  grid-template-columns: minmax(var(--space-4), 1fr) minmax(0, 25rem) minmax(var(--space-4), 1fr);
  align-content: center;
  min-height: 100vh;
  min-height: 100svh;
  padding-bottom: 8vh;
}
body[data-page="signin"] > * { grid-column: 2; }
body[data-page="signin"] .op-main { max-width: none; margin: 0; padding: 0 0 var(--space-10); }

.signin-mast {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-10);
}
.signin-mast .mark { display: block; flex: none; width: 2.5rem; height: 3.125rem; }
/* Which cluster and node this is, for someone who runs several. */
.signin-where { margin: 0.1rem 0 0; color: var(--text-muted); font-size: var(--fs-small); }
.signin-name {
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-strong);
  font-size: var(--fs-h3);
  line-height: var(--lh-tight);
  color: var(--text);
}

/* The post: from under the lantern down the whole column. The column's text
   starts where "Sereno" does. */
.signin {
  position: relative;
  padding: var(--space-6) 0 0 calc(2.5rem + var(--space-3));
}
.signin::before {
  content: "";
  position: absolute;
  left: calc(1.25rem - 0.75px);
  top: var(--space-2);
  bottom: 0;
  border-left: var(--border-width-strong) solid var(--border-strong);
}
.op-main .signin h1 { margin: 0 0 var(--space-5); }

.signin-form { display: grid; gap: var(--space-4); margin: 0; }
/* The fields sit on the surface, a step up from the page's ground. */
.signin-form .field { margin: 0; max-width: none; }
.signin-form .field input:not([type="checkbox"]):not([type="radio"]) { background: var(--surface); }

.signin-submit { margin: var(--space-2) 0 0; }

/* The OIDC slot: empty today, so it takes no space. */
.signin-alt:empty { display: none; }

/* ---- public pages: subscribe (screen 5), not found, the internal view ------- */

/* The internal view (tests and tools only; never on a public model). */
.internal table { border-collapse: collapse; margin: var(--space-2) 0; }
.internal td, .internal th {
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--border);
  text-align: left;
  font-size: var(--fs-small);
}
.internal h3 { margin-top: var(--space-4); font-size: var(--fs-ui); }

/* Subscribe: what the feed carries, and its address to copy. */
.subscribe .block-main, .not-found .block-main { display: flex; flex-direction: column; gap: var(--space-4); max-width: 42rem; }
.sub-lede { font-size: var(--fs-lede); line-height: 1.4; }
.feed-url {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: var(--border-width-strong) solid var(--border-strong);
  border-radius: var(--radius);
}
.feed-label { font-size: var(--fs-small); color: var(--text-muted); }
.feed-url > p { grid-column: 1; }
.feed-url code { display: block; margin-top: var(--space-1); font-size: var(--fs-body); overflow-wrap: anywhere; user-select: all; }
/* The copy button (hour-call.js shows it where a clipboard exists). */
.feed-url > .copy { grid-column: 2; grid-row: 1 / span 2; }
.sub-more { font-size: var(--fs-small); color: var(--text-muted); }
.back-link { display: inline-flex; align-items: center; min-height: var(--touch-min); }

@media (max-width: 46rem) {
  .sub-lede { font-size: var(--fs-ui); }
}

/* ---- the alert workspace and the archive (screen 7) ------------------------ */

/* The incident list (/app/) and the incident archive (/app/incidents/archive):
   the tabs, the filters, the polled #alerts region, row buttons, and the
   archive table. Incident states are ink; only the chip of an active P1 and
   the status badges carry colour. */

nav.tabs ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-5);
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
  border-bottom: 1px solid var(--border);
}
nav.tabs a {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  color: var(--text-muted);
  text-decoration: none;
}
nav.tabs a:hover { color: var(--text); }
nav.tabs a[aria-current="page"] {
  color: var(--text);
  font-weight: var(--weight-strong);
  box-shadow: inset 0 -2px 0 var(--text);
}

.alert-filters { margin-bottom: var(--space-2); }
.alerts-count { margin: var(--space-3) 0 var(--space-2); color: var(--text-muted); font-size: var(--fs-small); }
.alerts .empty { margin-top: var(--space-4); }

/* The archive on the kit's table (UT, audit S9, archive 1 to 4 and 6): the
   incident by its title, its machine name and kind under it, the service
   over its project; the words get the room and the short columns stay on
   one line. Under 46rem each row is a block: the chip and state, the
   title, then the facts after their names. */
.archive-table { min-width: 58rem; }
.archive-table .arc-peak { width: 3.5rem; }
.archive-table .arc-subject { min-width: 15rem; }
/* A title reads as a link at rest (re-audit, archive 1): underlined in the
   strong border colour, as the tables' other row links; the line takes the
   text colour on hover. */
.archive-table .arc-subject a { color: var(--text); font-weight: var(--weight-strong); text-decoration: underline; text-decoration-color: var(--border-strong); text-underline-offset: 0.18em; }
.archive-table .arc-subject a:hover { text-decoration-color: currentColor; }
.archive-table .arc-line { display: block; }
.archive-table .arc-where { color: var(--text-muted); overflow-wrap: anywhere; }
.archive-table :is(.arc-kind, .arc-project) { color: var(--text-muted); }
.archive-table .arc-state { white-space: nowrap; }
.archive-table .incstate { font-weight: var(--weight-regular); }
.archive-table .arc-service { min-width: 9rem; }
.archive-table .arc-onset { white-space: nowrap; }
.archive-table .arc-owner { min-width: 6rem; }
@media (max-width: 46rem) {
  .archive-table .arc-subject { order: 1; flex-basis: 100%; min-width: 0; }
  .archive-table .arc-service { order: 2; flex-basis: 100%; min-width: 0; }
  .archive-table .arc-service .arc-line { display: inline; }
  .archive-table .arc-project::before { content: ", "; }
  /* No service: the project alone (re-audit N17). */
  .archive-table .arc-none:has(+ .arc-project) { display: none; }
  .archive-table .arc-none + .arc-project::before { content: none; }
  .archive-table :is(.arc-onset, .arc-duration, .arc-count, .arc-owner) { order: 3; min-width: 0; }
}

nav.pager ul { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-4); margin: var(--space-2) 0 var(--space-4); padding: 0; list-style: none; }
nav.pager a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
nav.pager [aria-current="page"] { color: var(--text-muted); }

@media (max-width: 46rem) {
  .alert-filters select { min-height: var(--touch-min); }
}

/* ---- the alert detail (screen 8) ------------------------------------------- */

/* The heading (Alert_head), the actions, the polled #alert-live region
   (Vantages and Event_log), the comments, and the details beside them. Fun
   budget none: no voice, no motion. Only the status badges and the result
   bars carry colour. */


/* Actions: a row of buttons (polled with the heading, so empty while there
   is nothing to press), then disclosures (not a grid of cards). */
.od-actions { margin: 0 0 var(--space-6); }
.od-quick { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3); margin: 0 0 var(--space-3); }
.od-quick:empty { display: none; }
.od-act { margin: 0; }
.od-more { max-width: 40rem; border-bottom: 1px solid var(--border); }
.od-more:first-of-type { border-top: 1px solid var(--border); }
.od-more > summary { display: flex; align-items: center; gap: var(--space-2); color: var(--link); }
.od-more > summary .ico-chev { color: var(--text-muted); }
.od-more .od-act { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 var(--space-4); padding: var(--space-2) 0 var(--space-4); }
.od-more .od-act + .od-act { padding-top: 0; }
.od-act .field { margin: 0 0 var(--space-2); min-width: 12rem; }
.od-act .btn { margin-bottom: var(--space-2); }

/* The body: the actions, the live region and the comments, with the
   details beside them from the first button row down (no empty corner). */
.od-body { display: grid; grid-template-columns: minmax(0, 1fr) 22rem; gap: 0 var(--space-8); align-items: start; }
.op-main .od-body h2 { margin-top: var(--space-6); }
.od-live > section:first-of-type h2, .od-side > section:first-child h2 { margin-top: 0; }
.od-plain { max-width: var(--measure); }
.od-stages { margin: 0; padding: 0; list-style: none; }

/* Comments, add-only; the form sits outside the polled region. */
.od-comment-list { margin: 0 0 var(--space-4); padding: 0; list-style: none; }
.od-comment { padding: var(--space-3) 0; border-bottom: 1px solid var(--border); max-width: var(--measure); }
.od-comment:first-child { border-top: 1px solid var(--border); }
.od-comment-head { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); margin: 0 0 var(--space-1); }
.od-comment-who { font-weight: var(--weight-strong); }
.od-comment-head .data { color: var(--text-muted); }
.od-comment-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.od-comment-form { max-width: 40rem; }
.od-comment-form .field { max-width: none; }
.od-comment-form textarea { min-height: 7rem; resize: vertical; }

/* The details beside: a quiet fact list. */
.od-side { font-size: var(--fs-small); }
.od-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-4); margin: 0; }
.od-facts dt { color: var(--text-muted); }
.od-facts dd { margin: 0; overflow-wrap: anywhere; }
.od-facts .data { font-size: var(--fs-small); }
.od-why, .od-evidence { margin: var(--space-1) 0 0; padding-left: var(--space-4); }
.od-why li, .od-evidence li { margin: 0 0 var(--space-1); }
.od-why-line { margin: var(--space-1) 0 0; color: var(--text-muted); }

/* Technical details: the ids serenoctl and the API use, folded, each whole
   on its line so it copies in one go. */
.od-side .tech { margin-top: var(--space-4); }
.od-side .tech > summary { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--link); }
.od-side .tech > summary .ico-chev { color: var(--text-muted); }
.tech-facts { margin: var(--space-2) 0 0; }
.tech-facts dt { color: var(--text-muted); margin-top: var(--space-2); }
.tech-facts dd { margin: 0; }
.tech-id { font-size: var(--fs-small); overflow-wrap: anywhere; user-select: all; }

/* A past incident: what no longer applies (its tables are the kit's). */
.od-past-note {
  max-width: var(--measure);
  margin: 0 0 var(--space-5);
  padding: var(--space-2) var(--space-4);
  border-left: var(--border-width-strong) solid var(--link);
  background: var(--surface);
}

@media (max-width: 62rem) {
  .od-body { grid-template-columns: minmax(0, 1fr); }
  /* Under the comments: a rule and room, so the last button never touches
     the Details heading. */
  .od-side {
    max-width: var(--measure);
    margin-block-start: var(--space-8);
    padding-block-start: var(--space-5);
    border-top: 1px solid var(--border);
  }
}
@media (max-width: 46rem) {
  .od-quick .od-act { flex: 1 1 auto; }
  .od-quick .btn { width: 100%; }
  .od-more .od-act { display: block; }
  .od-act .field { min-width: 0; }
  .od-facts { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .od-facts dd { margin: 0 0 var(--space-2); }
}

/* ---- checks and a check's page (screens 9 and 10) -------------------------- */

/* A utility screen: type and colour only. Charts take every colour from
   classes: timeline runs by state (with height as the second channel),
   latency series in ink, indigo and muted with a dash pattern each, never a
   status colour. */
.checks-sum { margin: 0 0 var(--space-2); color: var(--text-muted); }
.checks-none { margin: var(--space-4) 0; }

/* A check's page: the status line, the facts, and the sections. */
.crumb { margin: 0; }
.crumb a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
.op-main .check-title { margin-top: 0; margin-bottom: var(--space-2); overflow-wrap: anywhere; }
.check-title .check-ns { color: var(--text-muted); font-weight: var(--weight-regular); }
.check-now { margin: 0 0 var(--space-5); }
.check-now .check-status { font-size: var(--fs-ui); gap: var(--space-1) var(--space-4); margin: 0; }
.check-since { color: var(--text-muted); font-size: var(--fs-small); }
.check-since .data { color: var(--text); }
.check-notes { margin: var(--space-2) 0 0; padding: 0; list-style: none; }
.check-notes li + li { margin-top: 0.15rem; }
.check-info {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--space-3) var(--space-5);
  margin: 0 0 var(--space-4);
  padding: var(--space-3) 0;
  border-top: var(--border-width-strong) solid var(--text);
  border-bottom: 1px solid var(--border);
}
.check-info dt { color: var(--text-muted); font-size: var(--fs-small); }
.check-info dd { margin: 0; overflow-wrap: anywhere; }
.check-info a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
table.matrix th[scope="row"], table.matrix td, table.results td { vertical-align: top; }
/* Wide tables scroll inside their box on a phone rather than wrap into
   tall rows. */
table.matrix { min-width: 48rem; }
table.results { min-width: 36rem; }
table.matrix .badge, table.results .badge { font-size: var(--fs-small); }
samp.data { overflow-wrap: anywhere; }
.dissent { color: var(--text); font-weight: var(--weight-strong); }
.ping-start { color: var(--text); }
details.more { margin: var(--space-2) 0 var(--space-4); }
details.more > summary { display: flex; align-items: center; color: var(--link); }
.check-incidents { margin: 0 0 var(--space-4); padding: 0; list-style: none; }
.check-incidents li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-4);
  min-height: var(--touch-min);
  border-bottom: 1px solid var(--border);
}
.check-incidents a { display: inline-flex; align-items: center; min-height: var(--touch-min); }
.op-main .svc-group-name, .op-main .svc-name { margin: 0; }
.op-main .svc-group { margin-top: var(--space-6); }
/* The 90 days per location (the kit's service rows) reflow by the width of
   their box, as on the public page by the window's. */
.check-days { container: check-days / inline-size; }
@container check-days (max-width: 60rem) {
  .svc-group-head, .svc { grid-template-columns: minmax(0, 1fr) auto; }
  .svc-group-name { grid-column: 1 / -1; }
  .day-axis, .svc-group-head.no-name > .day-axis { grid-column: 1; width: var(--strip); max-width: 100%; }
  .group-pct { grid-column: 2; }
  .svc-id { grid-column: 1 / -1; }
  .svc-days { grid-column: 1; width: var(--strip); max-width: 100%; }
  .svc-pct { grid-column: 2; }
}
@container check-days (max-width: 36rem) {
  .svc-group-head { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .day-axis, .svc-days { width: auto; }
  .group-pct { display: none; }
  .svc { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); }
  .svc-id { flex-direction: row; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 0 var(--space-3); }
  .svc-days { grid-column: 1; }
  .svc-pct { grid-column: 1; flex-direction: row; flex-wrap: wrap; align-items: baseline; gap: 0 var(--space-3); }
  .pct-label { display: inline; }
}

/* The 24-hour strips: runs by state, the height as the second channel. */
table.strips th { width: 8rem; white-space: nowrap; font-weight: var(--weight-regular); color: var(--text); }
table.strips td { width: 100%; min-width: 16rem; }
.tl-none { --tl: var(--status-unknown); }
.tl-up { --tl: var(--status-ok); }
.tl-impaired { --tl: var(--status-degraded); }
.tl-down { --tl: var(--status-down); }
svg .tl { fill: var(--tl); }
svg.strip { display: block; width: 100%; height: 1.25rem; }
.legend { display: flex; flex-wrap: wrap; gap: 0.25rem var(--space-4); margin: var(--space-1) 0 var(--space-3); padding: 0; list-style: none; font-size: var(--fs-small); color: var(--text-muted); }
.legend li { display: inline-flex; align-items: center; gap: 0.4em; }
svg.sw-bar { width: 0.6rem; height: 0.8rem; }
svg.sw-line, svg.sw-band, svg.sw-tick { width: 1.5rem; height: 0.5rem; }

/* Latency: lines are data, not status. Every chart of a check shares one
   y scale; its value labels sit in a gutter beside the drawing, so no line
   crosses them (re-audit N5): each at its grid line's height, which is the
   drawing's (Svg_chart: y0 34, y1 176, vh 208 units; level k at
   y1 - (y1 - y0) k / 4). */
.charts { display: grid; grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr)); gap: var(--space-2) var(--space-6); }
.chart-box { min-width: 0; container: chart-box / inline-size; }
.chart-frame { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0 var(--space-2); }
.chart-plot { min-width: 0; container: chart / inline-size; }
.chart-y { position: relative; width: 4.8em; font-size: var(--fs-micro); }
.chart-y .data {
  position: absolute;
  right: 0;
  color: var(--text-muted);
  font-size: var(--fs-micro);
  line-height: 1;
  white-space: nowrap;
  transform: translateY(-50%);
}
.chart-y .l1 { top: calc(140.5 / 208 * 100%); }
.chart-y .l2 { top: calc(105 / 208 * 100%); }
.chart-y .l3 { top: calc(69.5 / 208 * 100%); }
.chart-y .l4 { top: calc(34 / 208 * 100%); }
/* A narrow chart keeps every other value label, so they don't crowd. */
@container chart-box (max-width: 29rem) { .chart-y .axis-minor { display: none; } }
.chart-empty { margin: var(--space-2) 0 0; color: var(--text-muted); font-size: var(--fs-small); }
.op-main .chart-box h4 { margin: var(--space-2) 0 var(--space-1); font-size: var(--fs-small); font-weight: var(--weight-strong); color: var(--text-muted); }
svg.chart { display: block; width: 100%; height: auto; }
.chart .line, svg.sw-line .line { fill: none; stroke: var(--ln); stroke-width: 1.5; }
.chart .dot { fill: var(--ln); }
.chart .grid { stroke: var(--border); stroke-width: 1; }
.chart .axis {
  fill: var(--text-muted);
  font-size: var(--fs-micro);
  stroke: var(--ground);
  stroke-width: 3px;
  stroke-linejoin: round;
  paint-order: stroke;
}
/* The drawing is 720 units wide and scales to its plot box (.chart-plot),
   so as the box narrows its time labels grow in units: 12 px on screen at
   each step's narrowest box, a little more at its widest (Svg_chart leaves
   out a label that would run off the drawing at the largest). */
@container chart (max-width: 45rem) { .chart .axis { font-size: calc(var(--fs-micro) * 720 / 576); } }
@container chart (max-width: 36rem) { .chart .axis { font-size: calc(var(--fs-micro) * 720 / 480); } }
@container chart (max-width: 30rem) {
  .chart .axis { font-size: calc(var(--fs-micro) * 720 / 400); }
  /* A label that would run off the drawing from here down (axis-edge). */
  .chart .axis-edge { display: none; }
}
@container chart (max-width: 25rem) { .chart .axis { font-size: calc(var(--fs-micro) * 720 / 352); } }
@container chart (max-width: 22rem) {
  .chart .axis { font-size: calc(var(--fs-micro) * 720 / 288); }
  /* Labels that close would touch: every other one (axis-minor). */
  .chart .axis-minor { display: none; }
}
.chart .axis-data { font-family: var(--font-data); }
.chart .band, svg.sw-band .band { fill: var(--plane); opacity: 0.35; }
.ln-total { --ln: var(--text); }
.ln-dns { --ln: var(--text-muted); }
.ln-connect { --ln: var(--link); }
.ln-tls { --ln: var(--link); }
.ln-fb { --ln: var(--border-strong); }
.line.ln-total { stroke-width: 2; }
.line.ln-dns { stroke-dasharray: 1 3; stroke-linecap: round; }
.line.ln-connect { stroke-dasharray: 6 3; }
.line.ln-tls { stroke-dasharray: 2 2; }
.line.ln-fb { stroke-dasharray: 8 2 2 2; }
.op-main section.latency h3 { margin-top: var(--space-5); }

pre.config {
  margin: 0 0 var(--space-2);
  padding: var(--space-3) var(--space-4);
  overflow-x: auto;
  background: var(--surface);
  border-left: var(--border-width-strong) solid var(--border-strong);
}
.secret { color: var(--link); font-weight: var(--weight-medium); }

/* ---- the cluster (screen 11) ----------------------------------------------- */

/* One node's view (brief screen 11): whose view, the answer, the nodes, the
   evidence. No status colour outside the node badges and check badges: the
   answer is ink, calm and dense (fun budget: none during trouble). */

.cl-seen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--space-4);
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.cl-seen p { margin: 0; }
.cl-seen .data { color: var(--text); }
.cl-seen nav { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-3); }
.cl-seen ul { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); margin: 0; padding: 0; list-style: none; }
.cl-seen a { display: inline-flex; align-items: center; min-height: var(--touch-min); min-width: var(--touch-min); }
.cl-seen-hint { flex-basis: 100%; }

.cl-answer { max-width: var(--measure); padding-bottom: var(--space-2); }
.op-main .cl-headline { margin: var(--space-3) 0 var(--space-3); }
.cl-details { margin: 0; padding: 0; list-style: none; }
.cl-details li { padding: var(--space-1) 0 var(--space-1) var(--space-3); border-left: var(--border-width-strong) solid var(--border-strong); }

.cl-summary { margin: 0 0 var(--space-3); max-width: var(--measure); }
.cl-evidence { margin: 0; padding: 0; list-style: none; border-top: var(--border-width-strong) solid var(--text); }
.cl-group { padding: var(--space-2) 0 var(--space-3); border-bottom: 1px solid var(--border); }
.cl-why { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0 var(--space-4); margin: 0 0 var(--space-1); }
.cl-why-what { font-weight: var(--weight-strong); }
.cl-why-count { color: var(--text-muted); font-size: var(--fs-small); }
.cl-checks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
  gap: 0 var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}
.cl-checks li { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--space-3); min-width: 0; }
.cl-check { display: inline-flex; align-items: center; min-height: var(--touch-min); min-width: 0; overflow-wrap: anywhere; }
.cl-checks .badge { font-size: var(--fs-small); }
.cl-more, .cl-hidden { margin: var(--space-2) 0 0; color: var(--text-muted); font-size: var(--fs-small); }

@media (max-width: 46rem) {
  .cl-checks { grid-template-columns: minmax(0, 1fr); }
  .cl-checks li { justify-content: space-between; }
}

/* ---- escalation policies (screen 12) --------------------------------------- */

.pol-intro { max-width: var(--measure); margin: 0 0 var(--space-6); color: var(--text-muted); }
.pol-project, .pol-operator { margin-top: var(--space-8); }
.pol-project-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: var(--border-width-strong) solid var(--text);
}
.pol-project-head h2 { margin: 0; }
.pol-project-id { margin: 0; color: var(--text-muted); font-size: var(--fs-small); }
.pol-none, .pol-unrouted { max-width: var(--measure); margin: var(--space-3) 0 0; color: var(--text-muted); }
.pol-empty { margin-top: var(--space-8); max-width: var(--measure); }
.pol-empty p { color: var(--text-muted); }

/* ---- notification preferences (screen 13) ---------------------------------- */

/* The page: what happens at each priority, the channels, the choices. */

.pref-lede { margin: 0 0 var(--space-6); color: var(--text-muted); max-width: var(--measure); }
.pref-block { margin: 0 0 var(--space-8); }
.op-main .pref-block h2 { margin-top: 0; }
.pref-more { margin: var(--space-3) 0 0; color: var(--text-muted); font-size: var(--fs-small); max-width: var(--measure); }

.pref-rows { margin: 0; padding: 0; list-style: none; border-top: var(--border-width-strong) solid var(--text); }
.pref-row {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-2) var(--space-5);
  align-items: start;
  padding: var(--space-3) 0;
  border-bottom: var(--border-width) solid var(--border);
}
.pref-prio { padding-top: 0.1rem; }
.pref-label { margin: 0; color: var(--text-muted); font-size: var(--fs-small); }
.pref-sends p, .pref-fallback p { margin: 0; }
.pref-channels { margin: 0; padding: 0; list-style: none; }
.pref-why { margin: 0; color: var(--text-muted); font-size: var(--fs-small); padding-top: 0.1rem; }
.pref-note {
  display: flex;
  gap: var(--space-3);
  max-width: var(--measure);
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  color: var(--text);
  border-left: 3px solid var(--field-error-border);
  background: var(--surface);
}
.pref-note p { margin: 0; }
.pref-note > .ico { align-self: center; }

.pref-contact { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-5); margin: 0 0 var(--space-5); }
.pref-contact dt { color: var(--text-muted); }
.pref-contact dd { margin: 0; }
/* A stored key is shown by its last four characters; changing it opens
   the form (audit, preferences 4). */
.pref-change { margin: 0 0 var(--space-4); }
.pref-change > summary { display: inline-flex; align-items: center; gap: var(--space-2); min-height: var(--touch-min); color: var(--link); }
.pref-change > .pref-form { margin-top: var(--space-2); }

.pref-grid { border-top: var(--border-width) solid var(--border); max-width: 32rem; }
.pref-choice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--space-5);
  margin: 0;
  padding: var(--space-1) 0;
  border: 0;
  border-bottom: var(--border-width) solid var(--border);
  min-width: 0;
}
.pref-choice > legend { float: left; width: 4.5rem; padding: 0; }
.pref-choice .choice { margin-right: 0; }

@media (max-width: 46rem) {
  .pref-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-2) var(--space-4); }
  .pref-prio, .pref-why { grid-column: 1 / -1; }
  .pref-choice { gap: 0 var(--space-4); }
  .pref-choice > legend { width: 3.25rem; }
}

/* ---- projects and status pages (screen 14) --------------------------------- */

/* A read-only tree: the project in the margin, its namespaces beside it, each
   page a ledger row with its domains on the right. */

.pj-lede { max-width: var(--measure); margin: 0 0 var(--space-3); color: var(--text-muted); }
.pj-note { max-width: var(--measure); margin: 0 0 var(--space-2); font-size: var(--fs-small); color: var(--text-muted); }
.pj-note .data { font-size: var(--fs-small); color: var(--text); }

.pj {
  display: grid;
  grid-template-columns: 13rem minmax(0, 1fr);
  gap: var(--space-3) var(--space-6);
  margin-top: var(--space-8);
  padding-top: var(--space-3);
  border-top: var(--border-width-strong) solid var(--text);
}
.op-main .pj-margin h2 { margin: 0; }
.pj-margin p { margin: var(--space-1) 0 0; font-size: var(--fs-small); color: var(--text-muted); }
.pj-margin .data { font-size: var(--fs-small); }
.pj-margin .pj-publish { margin-top: var(--space-3); }
.pj-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
}

.pj-ns + .pj-ns { margin-top: var(--space-6); }
.op-main .pj-ns-head { margin: 0 0 var(--space-1); }
.pj-ns-head .data { font-size: var(--fs-ui); }
.pj-none { margin: 0; padding: var(--space-2) 0; font-size: var(--fs-small); color: var(--text-muted); }

/* A namespace's pages sit under its name on spacing alone (audit,
   projects 2): the project's rule and the rows' hairlines are enough. */
.pj-pages { margin: 0; padding: 0; list-style: none; }
.pj-page {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: var(--space-2) var(--space-5);
  padding: var(--space-3) 0;
  border-bottom: var(--border-width) solid var(--border);
}
.op-main .pj-page-title { margin: 0; font-size: var(--fs-ui); font-weight: var(--weight-strong); }
.pj-page-head .pj-page-state { margin: var(--space-1) 0; font-size: var(--fs-small); }
.pj-page-head p { margin: 0; font-size: var(--fs-small); color: var(--text-muted); }
.pj-page-head .data { font-size: var(--fs-small); color: var(--text); }
.pj-links { display: flex; flex-wrap: wrap; gap: 0 var(--space-4); margin: 0; padding: 0; list-style: none; }
.pj-page-problems { grid-column: 1 / -1; }

.pj-domains { margin: 0; padding: 0; list-style: none; }
.pj-domain {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 0 var(--space-4);
  padding: 0 0 var(--space-2);
}
.pj-domain + .pj-domain { padding-top: var(--space-2); border-top: var(--border-width) solid var(--border); }
.pj-domain-name { min-width: 0; overflow-wrap: anywhere; }
.pj-domain-facts { flex: 1 1 100%; min-width: 0; }
.pj-line { margin: 0; font-size: var(--fs-small); }
.pj-line .data { font-size: var(--fs-small); }
.pj-source, .pj-error { color: var(--text-muted); }
.pj-error code { overflow-wrap: anywhere; color: var(--text); }

/* Narrow by the space the tree has, not the window's. */
.pj-page-body { container: pj / inline-size; }
@container pj (max-width: 46rem) {
  .pj { grid-template-columns: minmax(0, 1fr); margin-top: var(--space-6); }
  .pj-page { grid-template-columns: minmax(0, 1fr); }
}
.pj-page-body > .problems { max-width: var(--measure); }

/* ---- users and groups (screen 15) ------------------------------------------ */

.ou-summary { margin: calc(-1 * var(--space-3)) 0 var(--space-3); color: var(--text-muted); }
.ou .data { font-size: var(--fs-small); }
.ou-gid { display: block; color: var(--text-muted); font-weight: var(--weight-regular); white-space: nowrap; }
.ou-off { display: inline-flex; align-items: center; gap: 0.4em; color: var(--text); font-size: var(--fs-small); font-weight: var(--weight-strong); }

/* The matrix: users by groups, a tick where they meet. */
/* The matrix (the kit's table): users by groups, a tick where they meet.
   The user column is capped and every group column has the same width, so
   a few groups don't leave one wide column (audit, users 2); the user
   column stays put when the matrix scrolls sideways. */
.tbl.ou-matrix { width: auto; max-width: 100%; }
.ou-matrix th, .ou-matrix td { padding: var(--space-2) var(--space-3); text-align: center; vertical-align: middle; }
.ou-matrix thead th { color: var(--text); }
.ou-matrix thead th + th { width: 8rem; min-width: 8rem; }
.ou-matrix th[scope="row"], .ou-matrix thead th:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  min-width: 11rem;
  max-width: 18rem;
  background: var(--ground);
  text-align: left;
  font-size: var(--fs-body);
  font-weight: var(--weight-regular);
}
.ou-matrix thead th:first-child { z-index: 2; font-size: var(--fs-small); color: var(--text-muted); }
.ou-matrix th[scope="row"] a, .ou-matrix thead th a { color: var(--text); text-decoration-color: var(--border-strong); text-underline-offset: 0.18em; }
.ou-matrix th[scope="row"] a:hover, .ou-matrix thead th a:hover { text-decoration-color: currentColor; }
.ou-matrix td, .ou-matrix thead th + th { border-left: var(--border-width) solid var(--border); }
.ou-matrix td.is-member { color: var(--text); }
.ou-matrix td.is-member .ico { width: 1.25rem; height: 1.25rem; }
.ou-matrix tfoot th, .ou-matrix tfoot td { color: var(--text-muted); font-size: var(--fs-small); border-bottom: 0; }

/* Users and groups: rows under a strong rule, like the alert list. */
.ou-list { margin: 0; padding: 0; list-style: none; border-top: var(--border-width-strong) solid var(--text); }
/* Rows wrap by their content, not only by the viewport, so they hold in
   any column width. */
.ou-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  align-items: flex-start;
  padding: var(--space-3) 0;
  border-bottom: var(--border-width) solid var(--border);
  scroll-margin-top: var(--space-4);
}
.ou-row > .ou-who { flex: 0 1 16rem; }
.ou-row > .ou-facts, .ou-row > .ou-role { flex: 1 1 18rem; }
.ou-row > .ou-act { flex: 0 0 auto; margin-left: auto; }
.ou-who { display: flex; flex-direction: column; align-items: flex-start; gap: 0.15rem; min-width: 0; }
.ou-name { color: var(--text); font-size: var(--fs-ui); font-weight: var(--weight-strong); }
.ou-email { color: var(--text-muted); font-size: var(--fs-small); overflow-wrap: anywhere; }
.ou-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-1) var(--space-4); margin: 0; font-size: var(--fs-small); }
.ou-facts dt { color: var(--text-muted); }
.ou-facts dd { margin: 0; }
.ou-lines { margin: 0; padding: 0; list-style: none; }
.ou-note { margin: 0; }
.ou-act { text-align: right; }
.ou-act form { margin: 0; }
.ou-self { margin: 0; font-size: var(--fs-small); }
:target > th { background: var(--surface); }

.ou-grant { align-items: center; padding: var(--space-1) 0; }
.ou-role { margin: 0; }

/* Each section's forms under its list (audit, users 1), on one grid of
   columns so the forms of every section line up. */
.ou-section > .ou-forms { margin-top: var(--space-6); }
.ou-forms { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(22rem, 100%), 1fr)); gap: var(--space-6) var(--space-8); }
.ou-form { border-top: var(--border-width-strong) solid var(--text); padding-top: var(--space-2); scroll-margin-top: var(--jump-clear); }
.op-main .ou-form h3 { margin: 0 0 var(--space-3); }
.ou-created { max-width: 40rem; margin: 0 0 var(--space-4); padding: var(--space-3) var(--space-4); background: var(--surface); border-left: var(--border-width-strong) solid var(--text); }
.ou-created p { margin: 0 0 var(--space-2); }
.ou-created p:last-child { margin: 0; }
.ou-created code { font-size: var(--fs-ui); overflow-wrap: anywhere; }

@media (max-width: 46rem) {
  .ou-row { gap: var(--space-2); }
  .ou-row > .ou-act { margin-left: 0; text-align: left; }
  /* Narrow: the action has a line of its own, so its confirmation stays a
     block there and opens in place, under its summary. */
  .ou-row > .ou-act { flex-basis: 100%; }
  .ou-row > .confirm, .ou-row > .confirm[open] { display: block; }
  .ou-grant { padding: var(--space-3) 0; }
  .ou-matrix th[scope="row"], .ou-matrix thead th:first-child { min-width: 8.5rem; }
  .ou-matrix th, .ou-matrix td { padding: var(--space-2); }
  .ou-forms { grid-template-columns: minmax(0, 1fr); }
}

/* ---- the profile ----------------------------------------------------------- */

/* Built like the preferences: plain sections under Fraunces headings, no
   cards (brief section 13). */
.me { max-width: 60rem; }
.me-lede { margin: 0 0 var(--space-6); color: var(--text-muted); max-width: var(--measure); }
.me-block { margin: 0 0 var(--space-8); }
.op-main .me-block h2 { margin-top: 0; }
.me-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: var(--space-2) var(--space-5); margin: 0; max-width: 44rem; }
.me-facts dt { color: var(--text-muted); }
.me-facts dd { margin: 0; }
.me-access { margin: 0; padding: 0; list-style: none; }
.me-access li + li { margin-top: var(--space-1); }
.me-form { max-width: 32rem; }
.me-form > .field-hint { margin: 0 0 var(--space-4); }

@media (max-width: 46rem) {
  .me-facts { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .me-facts dd { margin: 0 0 var(--space-3); }
}

/* ---- the internal status pages (Status_view) ------------------------------- */

/* What can be published comes first, one section per project: the new post
   and maintenance forms folded, each open post's update, each scheduled
   maintenance with its confirmation step. Folds are rows between rules, as
   on the alert detail. */
.publish-section { margin-bottom: var(--space-8); }
.op-main .publish-section h2 { margin-top: var(--space-4); }
.publish-folds { max-width: 44rem; margin: 0 0 var(--space-4); }
.publish { border-bottom: 1px solid var(--border); }
.publish:first-child { border-top: 1px solid var(--border); }
.publish > summary { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--space-2); color: var(--link); font-weight: var(--weight-strong); }
.publish > summary .ico-chev { flex: none; color: var(--text-muted); }
.publish > summary .muted { font-weight: var(--weight-regular); }
.publish-form { padding: var(--space-3) 0 var(--space-5); }
.publish-form .field, .publish-form .choices { max-width: none; }
.choice-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr)); gap: 0 var(--space-5); }
.choice-grid .choice { margin-right: 0; }
.field-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr)); gap: 0 var(--space-5); }
.publish-rows { max-width: 44rem; margin: 0; padding: 0; list-style: none; }
.publish-row { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--space-2) var(--space-4); padding: var(--space-2) 0; border-bottom: 1px solid var(--border); }
.publish-row:first-child { border-top: 1px solid var(--border); }
.publish-what { display: grid; gap: var(--space-1); margin: 0; }
.publish-title { font-weight: var(--weight-strong); }
.publish-what .muted { font-size: var(--fs-small); }

/* Each page as the public page draws its rows, then each service's
   members and incidents, folded. */
.status-page { margin-bottom: var(--space-10); }
.page-overall { margin: var(--space-2) 0 var(--space-4); }
.svc-detail { font-size: var(--fs-small); }
.svc-detail > summary { display: flex; align-items: center; gap: 0.35em; }
.svc-detail ul { margin: 0 0 var(--space-3) 1.35rem; }
.svc-detail li { padding: 0.15rem 0; }
/* Three levels (audit, status view 5): the page's title (h2), its sections
   as quiet labels over a rule, as the service groups are (h3), and each
   post's title (h4). */
.op-main .status-page > h3 {
  margin: var(--space-8) 0 var(--space-3);
  padding-bottom: var(--space-1);
  border-bottom: var(--border-width-strong) solid var(--text);
  color: var(--text-muted);
  font-size: var(--fs-small);
}
.status-page .post { margin: var(--space-3) 0 var(--space-6); }
.status-page .post h4 { margin: 0 0 var(--space-1); font-size: var(--fs-ui); }
.status-page .updates { margin-top: var(--space-3); }

/* ---- SLA reports ----------------------------------------------------------- */

/* The headline and range presets (the year grid is Sla_grid). An outage in
   the downtime list is red: it says down. */
:where(.op) .headline { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: var(--weight-strong); margin: .25rem 0 0; }
:where(.op) .headline.data { font-family: var(--font-data); font-weight: var(--weight-medium); }
/* The presets are three rows of three (audit, SLA 5): the rolling windows,
   then this and last month, quarter and year; each cell the same width. */
:where(.op) nav.ranges ul { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .5rem; max-width: 36rem; list-style: none; margin: 0 0 .75rem; padding: 0; }
:where(.op) nav.ranges a { display: flex; align-items: center; justify-content: center; height: 100%; padding: .15rem .6rem; border: 1px solid var(--border); border-radius: .3rem; text-align: center; text-decoration: none; }
:where(.op) nav.ranges a[aria-current="page"] { border-color: var(--link); font-weight: var(--weight-strong); }
@media (max-width: 46rem) {
  :where(.op) nav.ranges a { min-height: var(--touch-min); }
}
:where(.op) .st-major_outage, :where(.op) .st-partial_outage { color: var(--status-down-text); font-weight: var(--weight-strong); }

