:root {
  --bg: #0b1020;
  --card: #141a30;
  --text: #e7ecf5;
  --muted: #93a0b8;
  --border: #232a44;
  --accent: #6ea7ff;
  --up: #2ea043;
  --degraded: #d4a017;
  --dns: #e36209;
  --down: #d73a49;
  --unknown: #6e7681;
}
/* DESIGN.md §2.8 — light theme token overrides. Activated only by
   html[data-theme="light"], set via theme-toggle.js + localStorage.
   System dark/light preference is never consulted (§2.8 owner rule).
   All values pass WCAG AA against their respective surfaces (§2.1). */
html[data-theme="light"] {
  --bg: #f6f8fc;
  --card: #ffffff;
  --text: #1c2333;
  --muted: #5b677a;
  --border: #d8dfeb;
  --accent: #2563eb;
  --up: #1a7f37;
  --degraded: #9a6700;
  --dns: #bc4c00;
  --down: #cf222e;
  --unknown: #656d76;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  line-height: 1.5;
}
a { color: var(--accent); }
a:hover { text-decoration: underline; }
.container { max-width: 1120px; width: 100%; margin: 0 auto; padding: 1rem 1.5rem; }
header.site { padding: 1.25rem 0; border-bottom: 1px solid var(--border); }
header.site .container { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.75rem; }
header.site a.brand { color: var(--text); text-decoration: none; font-weight: 700; font-size: 1.05rem; letter-spacing: -0.01em; }
header.site .nav a { color: var(--muted); text-decoration: none; margin-left: 1.25rem; font-size: 0.92rem; }
header.site .nav a:hover { color: var(--text); }
/* Right-side header group: nav links, the location chip, and (next
   ticket) the theme toggle sit together so the brand/identity split
   survives the container's space-between. */
header.site .header-right { display: flex; align-items: center; flex-wrap: wrap; gap: 1rem; }
/* DESIGN.md §7.3 location chip — identity, not severity: it rides the
   --accent identity family (.pill.isp precedent)
   at eyebrow/micro-label scale. Informational, never a control: no
   hover state, no pointer. */
header.site .location-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: rgba(110, 167, 255, 0.08);
  color: var(--accent);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
header.site .location-chip .icon { width: 0.85rem; height: 0.85rem; }
header.site .location-chip .location-sep { color: var(--muted); }
/* Mobile (DESIGN.md §7.3): drop the city, keep the region — the chip
   stays one short token instead of wrapping the whole header. */
@media (max-width: 640px) {
  header.site .location-chip .location-place,
  header.site .location-chip .location-sep { display: none; }
}
/* DESIGN.md §7.3 theme toggle button — rightmost header control. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  flex-shrink: 0;
}
/* DETECTDOWN-111: same cascade trap as .service-card[hidden] — the
   inline-flex above would beat the UA's [hidden] rule, leaving a dead
   button visible when JS never runs to reveal it. */
.theme-toggle[hidden] { display: none; }
.theme-toggle:hover { color: var(--text); border-color: var(--accent); }
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.theme-toggle .icon { width: 1rem; height: 1rem; }
/* Dark mode (default): show sun (click → switch to light). */
.theme-toggle-moon { display: none; }
/* Light mode: show moon (click → switch to dark). */
html[data-theme="light"] .theme-toggle-sun { display: none; }
html[data-theme="light"] .theme-toggle-moon { display: flex; }
main { flex: 1; padding-bottom: 3rem; }
h1, h2, h3 { margin: 0 0 0.5rem; letter-spacing: -0.02em; }
h1 { font-size: clamp(2rem, 5vw, 2.75rem); }
h2 { font-size: 1.4rem; margin-top: 2rem; }
p { margin: 0 0 1rem; color: var(--muted); }
p.lead { color: var(--text); }

.hero { position: relative; padding: 3.5rem 0 2rem; text-align: center; }
/* DESIGN.md §7.4 hero background graph — a real 24h aggregate severity
   line behind the headline. Decorative only: aria-hidden, no pointer
   events, drawn in currentColor → --accent (never a status color), and
   opacity capped well under the 0.15 contrast ceiling in both themes.
   The content block stacks above it so text never loses contrast. */
.hero-graph {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  color: var(--accent);
  opacity: 0.12;
}
.hero-graph svg { display: block; width: 100%; height: 100%; }
.hero-graph .hero-graph-area { fill: currentColor; opacity: 0.45; }
.hero-graph .hero-graph-line { fill: none; stroke: currentColor; stroke-width: 1.5; }
.hero-content { position: relative; z-index: 1; }
.tagline { color: var(--muted); margin: 0 0 2rem; font-size: 1.1rem; max-width: 38rem; margin-left: auto; margin-right: auto; }
/* DESIGN.md §7.5 — the hero search is the catalog filter. Same input
   register as .services-filter (--card surface, --border outline,
   --accent focus, 8px radius) so it reads as the same affordance;
   centered at the old check-form width so the hero stays composed. */
.hero-filter { max-width: 540px; margin: 0 auto; }
.hero-filter input[type=search] {
  width: 100%;
  padding: 0.9rem 1rem;
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  border-radius: 8px;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.hero-filter input[type=search]:focus { border-color: var(--accent); }
form.check { display: flex; gap: 0.5rem; max-width: 540px; margin: 0 auto; flex-wrap: wrap; }
form.check label { width: 100%; text-align: left; font-size: 0.85rem; color: var(--muted); margin-bottom: 0.25rem; }
form.check input[type=text] {
  flex: 1 1 280px;
  padding: 0.9rem 1rem;
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  border-radius: 8px;
  outline: none;
}
form.check input[type=text]:focus { border-color: var(--accent); }
form.check button {
  padding: 0.9rem 1.5rem;
  font-size: 1rem;
  background: var(--accent);
  color: #0b1020;
  border: 0;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
}
form.check button:hover { filter: brightness(1.1); }

/* DESIGN.md §1: small, muted line of evidence under the search box —
   services monitored, regions polled, checks accumulated in the
   last 24h. No icons, no bold weights, no animation. Wraps on
   narrow viewports so the row never overflows on phones. */
.hero-stats {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 0.45rem;
  margin: 1.1rem auto 0;
  color: var(--muted);
  font-size: 0.85rem;
}
.hero-stats .sep { opacity: 0.5; }

.verdict {
  padding: 1.5rem;
  border-radius: 12px;
  margin: 1.5rem 0;
  display: flex;
  gap: 1rem;
  align-items: center;
  flex-wrap: wrap;
  border: 1px solid var(--border);
}
.verdict.up { background: rgba(46, 160, 67, 0.10); border-color: rgba(46, 160, 67, 0.5); }
.verdict.degraded { background: rgba(212, 160, 23, 0.10); border-color: rgba(212, 160, 23, 0.5); }
.verdict.dns { background: rgba(227, 98, 9, 0.10); border-color: rgba(227, 98, 9, 0.5); }
.verdict.down { background: rgba(215, 58, 73, 0.10); border-color: rgba(215, 58, 73, 0.5); }
.verdict .dot { width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0; }
.verdict.up .dot { background: var(--up); box-shadow: 0 0 0 4px rgba(46, 160, 67, 0.25); }
.verdict.degraded .dot { background: var(--degraded); box-shadow: 0 0 0 4px rgba(212, 160, 23, 0.25); }
.verdict.dns .dot { background: var(--dns); box-shadow: 0 0 0 4px rgba(227, 98, 9, 0.25); }
.verdict.down .dot { background: var(--down); box-shadow: 0 0 0 4px rgba(215, 58, 73, 0.25); }
.verdict-text { flex: 1; min-width: 200px; }
.verdict-label { font-size: 1.4rem; font-weight: 700; margin: 0 0 0.15rem; }
.verdict-detail { color: var(--muted); margin: 0; }

.banner {
  padding: 0.85rem 1.1rem;
  border-radius: 8px;
  margin: 1rem 0;
  background: rgba(212, 160, 23, 0.12);
  border: 1px solid rgba(212, 160, 23, 0.45);
  color: var(--text);
  font-size: 0.92rem;
}
.banner strong { color: var(--degraded); }
.banner.error {
  background: rgba(215, 58, 73, 0.10);
  border-color: rgba(215, 58, 73, 0.45);
}
.banner.error strong { color: var(--down); }

table.probes {
  width: 100%;
  border-collapse: collapse;
  margin-top: 1rem;
  font-size: 0.92rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
}
table.probes th, table.probes td {
  padding: 0.75rem 0.9rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
}
table.probes tr:last-child td { border-bottom: 0; }
table.probes th {
  color: var(--muted);
  font-weight: 500;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: rgba(255,255,255,0.02);
}
table.probes .ok { color: var(--up); font-weight: 600; }
table.probes .fail { color: var(--down); font-weight: 600; }
/* SPEC §2.1 proximity surface: a muted distance/tier per probe row
   plus a single muted caption above the table that summarizes how
   far the probes are. The DIST cell stays in body color (not a
   status accent) — distance is provenance, not severity, so it
   must not steal the eye from the ok/fail HTTP column. The cell is
   set in tabular-nums so a column of "~480 km" / "~3520 km"
   right-aligns its digits and "same continent" lines up cleanly. */
table.probes td.probes-dist {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.probes-proximity {
  margin: 0.4rem 0 0;
}
/* One-line explainer under the probes table for a sanctioned
   bot-block status (an expected-but-non-2xx code rendered green
   above). Muted, never a status accent — it's provenance copy,
   not a verdict. Absent entirely when every row is plain 2xx/3xx. */
.probes-footnote {
  margin: 0.5rem 0 0;
}

/* SPEC §9 phase 3 — browser-side complementary check. A single
   thin row visually anchored to the probes table: same --card +
   --border + table-typography family, so it reads as another panel
   of the same evidence block rather than a separate widget. Status
   is carried by the existing .pill tokens (no new colors); the
   sub-line is muted body copy. No animation — DESIGN.md §2.5 bans
   entrance motion, and the row's only state change (initial →
   success/failure) is a one-shot text swap, not a transition. */
.browser-check {
  margin-top: 0.85rem;
  padding: 0.7rem 0.95rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  font-size: 0.92rem;
}
.browser-check-line {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--text);
}
.browser-check-note {
  margin: 0.25rem 0 0;
  font-size: 0.85rem;
}

.meta { color: var(--muted); font-size: 0.88rem; }

/* Status-page sub-answer line: sits directly under the question-shaped
   H1 ("Is X down?") and answers in plain text. Tints just the leading
   word with the verdict accent so the binary answer reads first; the
   verdict banner below still carries pill + icon + label, so color is
   supplementary, not the sole signal (DESIGN.md §8). */
.status-answer {
  margin: 0 0 0.5rem;
  color: var(--text);
  font-size: 1.1rem;
  line-height: 1.45;
}
.status-answer .answer-lead { font-weight: 700; }
.status-answer.answer-up .answer-lead { color: var(--up); }
.status-answer.answer-degraded .answer-lead { color: var(--degraded); }
.status-answer.answer-dns .answer-lead { color: var(--dns); }
.status-answer.answer-down .answer-lead { color: var(--down); }
.status-answer.answer-unknown .answer-lead { color: var(--muted); }

/* Lucide icons render as `currentColor` strokes — every utility class
   sets ``color`` and the SVG inherits it. ``object-fit: contain`` keeps
   the square viewBox crisp at the rendered size. */
.icon {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  vertical-align: -0.15em;
  color: currentColor;
}
.icon-inline { width: 1em; height: 1em; }
.icon-up { color: var(--up); }
.icon-degraded { color: var(--degraded); }
.icon-dns { color: var(--dns); }
.icon-down { color: var(--down); }
.icon-muted { color: var(--muted); }

.section-header { margin: 2.5rem 0 1.25rem; }
.section-header h2 { margin: 0 0 0.4rem; font-size: 1.5rem; }
.section-sub { color: var(--muted); margin: 0; max-width: 42rem; }

/* /services browse page. The header sits at the natural top of
   main (no hero strip) so it leans on default h1 weight to mark
   the page identity; the grid below reuses the home .service-grid
   tokens — same min-220px column, same card anatomy — because the
   only thing differing between the two pages is the catalog the
   cards walk. The muted .services-foot keeps the long list from
   dead-ending: a visitor who scrolls past 50 cards still has a
   single CTA back to /check. */
.services-header { margin-top: 2rem; }
.services-header h1 { font-size: clamp(1.75rem, 4vw, 2.25rem); margin: 0 0 0.4rem; }
/* ROADMAP.md M5 "Browse by category" discovery row in the
   /services header. A single muted inline line — the categories
   are the eye-catch, the leading label is the orientation. Reuses
   --accent for the links so the row reads as the same affordance
   as every other in-product link. Categories never carry a badge
   on cards (M5 anti-pattern); this row is the only place the
   category vocabulary surfaces to visitors. */
.services-categories {
  margin: 0.4rem 0 0;
  color: var(--muted);
  font-size: 0.92rem;
}
.services-categories-label { margin-right: 0.35rem; }
.services-categories a { color: var(--accent); text-decoration: none; }
.services-categories a:hover { text-decoration: underline; }
/* DETECTDOWN-50 in-page filter. Same family as the home check
   input — --card surface, --border outline, --accent on focus,
   8px radius — so a visitor who used the hero search recognizes
   the same affordance above the grid. The input is left-anchored
   and capped at 28rem so it reads as a tool, not a second hero;
   the surrounding container already caps the row at 1120px. */
.services-filter { margin: 0 0 1.25rem; }
.services-filter input[type=search] {
  width: 100%;
  max-width: 28rem;
  padding: 0.9rem 1rem;
  font-size: 1rem;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--text);
  border-radius: 8px;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.services-filter input[type=search]:focus { border-color: var(--accent); }
.services-no-results {
  margin: 0 0 1.5rem;
  color: var(--muted);
  font-size: 0.92rem;
}
.services-foot { margin: 0.5rem 0 2.5rem; text-align: center; font-size: 0.92rem; }
.services-foot a { color: var(--accent); text-decoration: none; }
.services-foot a:hover { text-decoration: underline; }

/* DETECTDOWN-57 whole-grid empty state. Shown only when zero cards
   carry a verdict (fresh deploy, poller between recoveries) so the
   page never reads as broken. Same --card/--border surface family
   as the cards below, strictly --muted, no status tint, no motion
   (§2.5) — it's reassurance, not an alert; the verdict pills keep
   the spotlight. Bottom margin matches the grid gap so the banner
   reads as the row above the cards, not a second hero. */
.catalog-empty-banner {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.85rem 1.05rem;
  margin: 0 0 0.85rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  color: var(--muted);
  font-size: 0.92rem;
}

/* Service grid — the downdetector-style dense card grid. */
.service-grid {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  margin: 0 0 3rem;
}
.service-card {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem 1.05rem 0.85rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.12s ease, transform 0.12s ease, background 0.12s ease;
}
/* DETECTDOWN-111: the explicit display above beats the UA's
   [hidden] { display: none }, so the filter scripts' hidden toggling
   needs this higher-specificity override to actually hide cards. */
.service-card[hidden] { display: none; }
.service-card:hover {
  border-color: var(--accent);
  text-decoration: none;
  transform: translateY(-1px);
  background: #171f3a;
}
.service-card-head { display: flex; align-items: center; gap: 0.75rem; }
.service-logo {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
}
.service-logo img {
  width: 22px;
  height: 22px;
  object-fit: contain;
}
.service-initial {
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--muted);
  letter-spacing: -0.01em;
}
.service-meta { display: flex; flex-direction: column; min-width: 0; }
.service-name {
  font-weight: 600;
  font-size: 0.98rem;
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.service-verdict {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--muted);
  font-size: 0.78rem;
}
.service-spark {
  margin-top: auto;
  padding-top: 0.4rem;
  border-top: 1px solid var(--border);
}
.service-spark .sparkline { display: block; }
.service-spark .sparkline-empty {
  background: repeating-linear-gradient(90deg,
    rgba(255,255,255,0.04) 0 2px, transparent 2px 6px);
  border-radius: 4px;
}
/* Sparkline bar colors per severity bucket. */
.sparkline .spark-up { fill: rgba(46,160,67,0.55); }
.sparkline .spark-degraded { fill: var(--degraded); }
.sparkline .spark-dns { fill: var(--dns); }
.sparkline .spark-down { fill: var(--down); }

/* SPEC §6 visitor-report overlay. Sits between the verdict banner
   and the 24h timeline. The line is intentionally *not* tinted with
   any verdict color so it never reads as the verdict — reports are
   an overlay and an escalation trigger, never the answer. The
   button is accent-bordered (DESIGN.md §5.2: no icon next to the
   repeated CTA). */
.report-overlay {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin: 1.25rem 0 0;
  padding: 0.85rem 1.05rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.report-overlay-line {
  margin: 0;
  color: var(--muted);
  font-size: 0.92rem;
  flex: 1 1 280px;
  line-height: 1.45;
}
.report-overlay-line strong {
  color: var(--text);
  font-weight: 600;
}
.report-form { margin: 0; flex-shrink: 0; }
.report-button {
  padding: 0.55rem 1rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.report-button:hover {
  background: rgba(110, 167, 255, 0.10);
}
.report-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* SPEC §2.1 per-region verdict grid on /status/<slug>. DESIGN.md §3
   forbids full-card status tints at grid density — tiles read
   state from a 9px pill + a colored border, never a washed surface.
   Continent label is uppercase-micro per the table-header rhythm so
   the verdict line carries the visual weight. */
.region-grid {
  display: grid;
  gap: 0.6rem;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin: 1.5rem 0;
}
.region-tile {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.85rem 0.95rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.region-tile.status-up { border-color: rgba(46, 160, 67, 0.35); }
.region-tile.status-degraded { border-color: rgba(212, 160, 23, 0.35); }
.region-tile.status-dns { border-color: rgba(227, 98, 9, 0.35); }
.region-tile.status-down { border-color: rgba(215, 58, 73, 0.35); }
.region-tile-head {
  margin: 0;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.region-tile-verdict {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text);
  font-size: 0.98rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.region-tile.status-unknown .region-tile-verdict {
  color: var(--muted);
  font-weight: 500;
}

/* Uptime tick-bar strips for the status page incident timeline
   (SPEC §2.2 + DETECTDOWN-51, DESIGN.md §4.7). Two strips sit under
   one <h2>: the 24h strip answers "is it down right now?", the 7-day
   strip answers "is this a repeat outage?". Sub-headers ride the
   uppercase-micro register used by table headers and region-tile
   heads (DESIGN.md §2.2) so they read as labels, not section
   titles competing with the <h2>; the uptime figure right-aligns on
   the same line. */
.status-timeline { margin: 1.75rem 0 2rem; }
.status-timeline h2 { margin: 0 0 0.6rem; }
.status-timeline h3.status-timeline-subhead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin: 1.25rem 0 0.45rem;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}
.status-timeline h3.status-timeline-subhead:first-of-type {
  margin-top: 0.4rem;
}
.status-timeline-uptime { font-variant-numeric: tabular-nums; }
.status-timeline-strip {
  padding: 0.6rem 0.8rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.status-timeline-strip .tickbar { display: block; }
.status-timeline-strip .tickbar-empty {
  background: repeating-linear-gradient(135deg,
    rgba(255,255,255,0.04) 0 6px, transparent 6px 14px);
  border-radius: 8px;
}
/* DESIGN.md §4.7: categorical color, full-height ticks. Healthy green
   is slightly translucent so a clean week recedes and an incident bar
   pops; missing hours are --unknown at low alpha — visibly distinct
   from healthy, never green, never counted in the uptime figure. */
.tickbar .tick-up { fill: var(--up); fill-opacity: 0.62; }
.tickbar .tick-degraded { fill: var(--degraded); }
.tickbar .tick-dns { fill: var(--dns); }
.tickbar .tick-down { fill: var(--down); }
.tickbar .tick-missing { fill: var(--unknown); fill-opacity: 0.38; }
.status-timeline-caption { margin: 0.55rem 0 0; }

/* SPEC §5 past-incidents list. Each row is a single .incident-row
   inside a clean .incident-list (no bullets, no list indent). The
   row's left border is the only colored surface — DESIGN.md §3
   anti-pattern: don't tint the full card. Verdict identity is
   carried by the .pill at the start of the headline; the border
   echoes it without washing the row. */
.past-incidents { margin: 0 0 2rem; }
.past-incidents h2 { margin: 0 0 0.6rem; }
.incident-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.incident-row {
  padding: 0.7rem 0.95rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: 8px;
}
.incident-row.row-up { border-left-color: var(--up); }
.incident-row.row-degraded { border-left-color: var(--degraded); }
.incident-row.row-dns { border-left-color: var(--dns); }
.incident-row.row-down { border-left-color: var(--down); }
/* M11: each row links to its permanent incident page — the anchor
   carries the row surface itself (same move as .now-card). Hover and
   focus echo the .now-card accent border, without the lift: the list
   is denser than the card stack. */
a.incident-row {
  display: block;
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.12s ease;
}
a.incident-row:hover,
a.incident-row:focus-visible {
  border-color: var(--accent);
  text-decoration: none;
}
.incident-headline {
  margin: 0 0 0.15rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--text);
  font-size: 0.98rem;
  font-weight: 600;
  flex-wrap: wrap;
}
.incident-headline .incident-verdict { letter-spacing: -0.01em; }
.incident-headline .incident-scope { font-weight: 400; }
.incident-timing { margin: 0; font-size: 0.85rem; }

/* ROADMAP M9 — "Embed this status" block near the page footer. The
   badge preview is the live /badge/<slug>.svg, not a mockup; the
   readonly input carries the anchor-wrapped HTML (the backlink is the
   payoff). Input reuses the check-box register — --card surface,
   --border outline, --accent focus, 8px radius — so it reads as the
   same control family; the monospace stack is the one deliberate
   difference because the value is code, not a query. The Copy button
   ships hidden and copy-embed.js reveals it (JS-progressive). */
.status-embed { margin: 0 0 2rem; }
.status-embed h2 { margin: 0 0 0.35rem; }
.status-embed-caption { margin: 0 0 0.8rem; }
.status-embed-preview { margin: 0 0 0.6rem; }
.status-embed-preview img { display: block; }
.status-embed-copy {
  display: flex;
  gap: 0.5rem;
  max-width: 640px;
}
.status-embed-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.55rem 0.75rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
  color: var(--muted);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.status-embed-input:focus {
  border-color: var(--accent);
  color: var(--text);
}
.status-embed-button {
  flex-shrink: 0;
  padding: 0.55rem 1rem;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--accent);
  background: transparent;
  border: 1px solid var(--accent);
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.status-embed-button:hover {
  background: rgba(110, 167, 255, 0.10);
}
.status-embed-button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ROADMAP M6 /now list — one card per currently-open incident.
   Fuses the .service-card head anatomy (logo box + name) with the
   .incident-row left-border accent; the whole card is the link
   target into /status/<slug>, same as the grid. No sparkline on
   purpose — the page answers "right now", not "last 24h"
   (DESIGN.md §7.2). Capped at 640px: a single-column incident list
   at container width reads as stretched banners, not cards. */
.now-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-width: 640px;
  margin: 0 0 1rem;
}
.now-card {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.85rem 1.05rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: 12px;
  text-decoration: none;
  color: var(--text);
  transition: border-color 0.12s ease, transform 0.12s ease, background 0.12s ease;
}
.now-card:hover,
.now-card:focus-visible {
  border-color: var(--accent);
  text-decoration: none;
  transform: translateY(-1px);
  background: #171f3a;
}
.now-card.row-degraded { border-left-color: var(--degraded); }
.now-card.row-dns { border-left-color: var(--dns); }
.now-card.row-down { border-left-color: var(--down); }
.now-card-body { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.now-card-headline {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.98rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  flex-wrap: wrap;
}
.now-card-headline .now-card-verdict { font-weight: 400; color: var(--muted); }
.now-card-meta { margin: 0; font-size: 0.85rem; }
/* /now all-clear: the .legend-card surface standing alone, capped
   at the list width so the panel doesn't stretch container-wide. */
.now-all-clear { max-width: 640px; }
/* ROADMAP M8 /now "Recently resolved" — the same .now-card anatomy
   below the open list (or the all-clear card on a quiet day). Cards
   keep the peak-verdict pill + left border, like the /status
   past-incidents rows: history wears the verdict it earned. The h2
   and the "Resolved … · lasted …" meta line carry the
   resolved-not-live distinction; no extra color or icon. */
.now-resolved { margin-top: 2.2rem; }
.now-resolved h2 { margin: 0 0 0.35rem; }
.now-resolved-sub { margin: 0 0 0.9rem; max-width: 640px; }

/* Pills — reused on cards, legend, and the old catalog block. */
.pill {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--muted); flex-shrink: 0;
  display: inline-block;
}
.pill.up { background: var(--up); box-shadow: 0 0 0 3px rgba(46,160,67,0.18); }
.pill.degraded { background: var(--degraded); box-shadow: 0 0 0 3px rgba(212,160,23,0.18); }
.pill.dns { background: var(--dns); box-shadow: 0 0 0 3px rgba(227,98,9,0.18); }
.pill.down { background: var(--down); box-shadow: 0 0 0 3px rgba(215,58,73,0.18); }
.pill.unknown { background: var(--unknown); }
/* SPEC §2.4 ISP-match badge. Overrides the 9px-dot base shape into
   a text pill: identity, not severity, so it lives on --accent (the
   brand color) rather than a status color. Sits inline next to the
   probe's network name. */
.pill.isp {
  width: auto; height: auto;
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  background: var(--accent); color: #0b1020;
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.02em;
  margin-left: 0.5rem; vertical-align: 1px;
}
/* Headline above the probes table when at least one probe matched
   the visitor's ASN (SPEC §2.4 differentiator). Muted line + accent
   radio-tower icon so it reads as evidence provenance, not status. */
.isp-headline {
  display: inline-flex; align-items: center; gap: 0.45rem;
  margin: 0.25rem 0 0; color: var(--muted); font-size: 0.92rem;
}
.isp-headline .icon { color: var(--accent); }

/* Status legend (DESIGN.md §7.6) — on the home page the legend is one
   wrapping row of five compact chips, a key rather than a section. It
   never grows back into cards. */
.legend { margin-bottom: 3rem; }
.legend-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}
.legend-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.35rem 0.85rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  cursor: help;
}
.legend-chip .icon { width: 0.95rem; height: 0.95rem; flex-shrink: 0; }
/* The .legend-grid/.legend-card tiles below no longer render on the
   home page; they remain the reading-context surfaces on /how and the
   /now all-clear panel. */
.legend-grid {
  display: grid;
  gap: 0.85rem;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.legend-card {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
  padding: 1rem 1.1rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.legend-icon {
  width: 36px; height: 36px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 10px;
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--border);
  flex-shrink: 0;
}
.legend-icon .icon { width: 1.1rem; height: 1.1rem; }
.legend-label {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 0.25rem;
}
.legend-detail { color: var(--muted); margin: 0; font-size: 0.9rem; line-height: 1.45; }
/* Exactly two flex items — the shield icon and one sentence-bearing
   <span>. Loose inline children would each become a flex item and the
   gap would shatter the sentence (the pre-§7.6 fragmentation bug). */
.legend-foot {
  margin: 1rem 0 0;
  color: var(--muted);
  font-size: 0.92rem;
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
}
.legend-foot .icon { color: var(--up); flex-shrink: 0; margin-top: 0.2em; }

/* 404 page: a quieter variant of the landing hero (no eyebrow chip —
   this is an error state) followed by a compact row of popular
   services. Keeps the visitor inside the product instead of dead-
   ending on a back-link. Tokens are reused from .hero / .service-card
   so the page reads as the same surface, not a separate error theme. */
.not-found-hero { padding: 3rem 0 1.5rem; text-align: center; }
.not-found-hero h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); }
.not-found-hero .tagline { margin-bottom: 1.75rem; }
.not-found-hero code {
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.95em;
  color: var(--text);
}
.not-found-suggestion {
  margin: 1rem auto 0;
  color: var(--muted);
  font-size: 0.95rem;
}
.not-found-suggestion a { color: var(--accent); }

.popular-services { margin: 1.5rem 0 3rem; }
.popular-services .section-header { text-align: center; margin-top: 1.25rem; }
.popular-services .section-header h2 { font-size: 1.05rem; color: var(--muted); font-weight: 500; letter-spacing: 0.01em; }
.popular-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
}
.popular-item {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.5rem 0.9rem 0.5rem 0.55rem;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  text-decoration: none;
  font-size: 0.95rem;
  transition: border-color 0.12s ease, background 0.12s ease, transform 0.12s ease;
}
.popular-item:hover {
  border-color: var(--accent);
  text-decoration: none;
  background: #171f3a;
  transform: translateY(-1px);
}
.popular-logo {
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border);
  border-radius: 6px;
  flex-shrink: 0;
  overflow: hidden;
}
.popular-logo img { width: 16px; height: 16px; object-fit: contain; }
.popular-logo .service-initial { font-size: 0.7rem; }
.popular-name { font-weight: 500; letter-spacing: -0.01em; }

.reject-detail {
  background: var(--card);
  border: 1px solid var(--border);
  padding: 1rem;
  border-radius: 8px;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.85rem;
  color: var(--muted);
}

footer.site {
  padding: 1.5rem 0;
  color: var(--muted);
  font-size: 0.82rem;
  text-align: center;
  border-top: 1px solid var(--border);
}
footer.site p { margin: 0 0 0.35rem; color: var(--muted); }

@media (max-width: 540px) {
  .verdict { flex-direction: column; align-items: flex-start; }
  table.probes { font-size: 0.85rem; }
  table.probes th, table.probes td { padding: 0.55rem 0.6rem; }
}
