/* CyberPulse-AI. Hand-written, no framework, no build step. */

@font-face { font-family: "JetBrains Mono"; font-weight: 400; font-style: normal; font-display: swap; src: url("fonts/jetbrains-mono-400.woff2") format("woff2"); }
@font-face { font-family: "JetBrains Mono"; font-weight: 600; font-style: normal; font-display: swap; src: url("fonts/jetbrains-mono-600.woff2") format("woff2"); }

:root {
  /* Slate and cyan (October 2026 redesign). Two backgrounds and only two: the page and the
     surface every panel and card is drawn on. Anything inset inside a surface (a chip, a
     headline row, a gauge) goes back to the page colour, so every foreground on the site sits
     on one of exactly two colours and every ratio in PLAN.md 8.1 is measured on both.

     The spec hues are the tokens, and they are used as they are for fills, borders, bars and
     icons. A hue that is too dark to carry *text* keeps its token and gets a text tint beside
     it, lightened toward white by the least amount that clears 4.5:1: only critical needed
     one (#EF4444 is 3.56:1 as badge text on its own 10% tint; #F26B6B, 21% toward white, is
     4.53:1). --line draws container edges, which carry no information; --line-strong draws
     the edge of anything you can operate, so it is held to 3:1 (WCAG 1.4.11) on both. */
  --bg-page: #0F172A;
  --bg-surface: #1E293B;
  --line: #334155;
  --line-strong: #64748B;
  --text-head: #F8FAFC;
  --text: #E2E8F0;
  --text-dim: #CBD5E1;
  --text-muted: #94A3B8;
  --accent: #06B6D4;
  /* The AI beat's own hue, used by the -AI in the wordmark, the AI beat chip and the AI
     significance badge. Pink stays reserved for model inference (.chip--ai, .detail-col--ai),
     so "AI story" and "AI-generated claim" are never the same colour. */
  --beat-ai: #A78BFA;
  --ai-inference: #F472B6;
  --sev-critical: #EF4444;
  --sev-critical-text: #F26B6B;
  --sev-high: #F59E0B;
  --sev-medium: #FBBF24;
  --sev-low: #10B981;
  --sev-info: #38BDF8;

  /* Two faces, one job each. JetBrains Mono, self-hosted, for data and system text: tags,
     CVEs, scores, agent names, timestamps and every navigation or control label. The reader's
     own UI face for everything that is read as prose. */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

  --radius: 8px;
  --radius-sm: 4px;
  --gap: 16px;
  --measure: 1480px;
}

*, *::before, *::after { box-sizing: border-box; }
html { color-scheme: dark; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background-color: var(--bg-page);
  color: var(--text);
  font: 400 16px/1.6 var(--font-sans);
  /* break-word, not anywhere: "anywhere" splits ordinary words mid-syllable at any width. */
  overflow-wrap: break-word;
}

h1, h2, h3, h4, p { margin: 0; }
a { color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--text-head); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
/* Long opaque strings (event ids, CVEs, URLs) are the only place breaking mid-token beats
   overflowing the panel. */
.mono, .detail__id { overflow-wrap: anywhere; }
.wrap { max-width: 1320px; margin: 0 auto; padding: 0 20px; }
.skip-link { position: absolute; left: -999px; top: 0; z-index: 200; background: var(--accent); color: var(--bg-page); padding: 8px 12px; font: 600 13px/1.2 var(--font-mono); }
.skip-link:focus { left: 8px; top: 8px; }
[hidden] { display: none !important; }
/* Read out, not shown: the word that makes a badge make sense to a screen reader. */
.visually-hidden { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* ------------------------------------------------------------------ header */

/* Sticky on a wide screen, because it carries the tabs, the scope and the filters, and a control
   a reader has to scroll back up to find is a control they stop using. Static under 720px,
   where it wraps to several rows and would cover too much of a phone. */
.site-header { position: sticky; top: 0; z-index: 90; background: var(--bg-page); border-bottom: 1px solid var(--line); }
@media (max-width: 720px) { .site-header { position: static; } }
/* Row one is a grid, so the four parts keep their places as it narrows instead of wrapping in
   whatever order fits: brand, tabs, status and controls on one line on a wide screen; the status
   drops under them below 1180px; and below 860px the tabs take a line of their own. */
.site-header__row { display: grid; grid-template-columns: max-content max-content minmax(0, 1fr) max-content; grid-template-areas: "brand tabs status controls"; align-items: center; gap: 8px 20px; max-width: var(--measure); margin: 0 auto; padding: 10px 20px 8px; }
.brand { grid-area: brand; font: 700 18px/1.2 var(--font-sans); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-head); white-space: nowrap; }
.brand__mark { color: var(--accent); }
.brand__ai { color: var(--beat-ai); }
.brand--link { text-decoration: none; }
.brand--link:hover { color: var(--accent); }
.site-header__controls { grid-area: controls; display: flex; gap: 8px; justify-content: flex-end; }
.site-header__controls .btn[aria-current] { border-color: var(--accent); color: var(--accent); }

/* The views. A tab is a link, and the open one carries aria-current="page"; it is marked by a
   filled ground and an accent bar under it, not by its colour alone. */
.tabs { grid-area: tabs; display: flex; gap: 2px; min-width: 0; }
.tab { display: inline-flex; align-items: center; min-height: 36px; padding: 8px 12px; font: 600 12px/1 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; text-decoration: none; white-space: nowrap; color: var(--text-dim); border-bottom: 2px solid transparent; border-radius: 6px 6px 0 0; }
.tab:hover { color: var(--accent); background: var(--bg-surface); }
.tab[aria-current] { color: var(--text-head); background: var(--bg-surface); border-bottom-color: var(--accent); }

/* "As at when", in the header so it is on screen whenever the scope or the filters are. */
.status-strip { grid-area: status; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0 12px; align-items: baseline; min-width: 0; font: 400 13px/1.5 var(--font-mono); color: var(--text-dim); }
.status-strip__label { font: 600 11.5px/1.5 var(--font-mono); letter-spacing: 0.08em; color: var(--text-muted); }
.status-strip__value { color: var(--text-head); }
.status-strip__local { color: var(--text-muted); }
/* A snapshot older than two hours (pages.js `staleSince`): the header says so on every view. */
.status-strip__stale { font-weight: 600; letter-spacing: 0.04em; color: var(--sev-medium); }

@media (max-width: 1180px) {
  .site-header__row { grid-template-columns: max-content minmax(0, 1fr) max-content; grid-template-areas: "brand tabs controls" "status status status"; }
  .status-strip { justify-content: flex-start; }
}
/* The tabs scroll sideways rather than wrap or fold: five words, all always one swipe away. */
@media (max-width: 860px) {
  .site-header__row { grid-template-columns: minmax(0, 1fr) max-content; grid-template-areas: "brand controls" "tabs tabs" "status status"; }
  .tabs { overflow-x: auto; scrollbar-width: thin; margin: 0 -20px; padding: 0 20px; }
}
/* A phone: the brand and the two buttons beside it shrink to share the first line, and the brand
   is cut short rather than run under HISTORY on the narrowest screens. */
@media (max-width: 440px) {
  .site-header__row { column-gap: 12px; }
  .brand { min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 15px; letter-spacing: 0.05em; }
  .site-header__controls { gap: 6px; }
  .site-header__controls .btn { padding: 6px 9px; min-height: 30px; font-size: 11px; }
}

.btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; font: 600 12px/1 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text); background: var(--bg-surface); border: 1px solid var(--line-strong); border-radius: 6px; padding: 8px 12px; min-height: 34px; cursor: pointer; }
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn[aria-pressed="true"], .btn[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.btn--small { padding: 6px 10px; min-height: 30px; font-size: 11.5px; }
/* Buy Me a Coffee's own yellow, sized the same as every other header button rather than an
   embedded logo image, so it can't render at the image's native size if its CSS fails to load. */
.btn--coffee { background: #FFDD00; border-color: #FFDD00; color: #0b0d12; }
.btn--coffee:hover { background: #ffe657; border-color: #ffe657; color: #0b0d12; }
/* A .btn that is a link needs its underline removed. */
a.btn { text-decoration: none; }

/* Row two: the scope, the beat, FEEDS, TAG FILTERS, KEY ONLY and the count. One line on a wide
   screen, wrapping as it narrows. The tag panel opens over the page from here (position:
   absolute against .filterbar); the feeds panel hangs from its own summary. */
.filterbar { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; max-width: var(--measure); margin: 0 auto; padding: 0 20px 10px; }
.scope-row { display: flex; align-items: center; margin: 0; padding: 0; border: 0; min-width: 0; }
/* Floated so it lays out as a label beside the segments instead of on the fieldset's border. */
.scope-row__legend, .beat-row__label { float: left; margin: 0 10px 0 0; padding: 0; font: 600 11px/1 var(--font-mono); letter-spacing: 0.12em; color: var(--text-muted); }
.beat-row { display: flex; align-items: center; }
.seg { position: relative; display: inline-flex; }
.seg input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.seg__label, .seg-btn { display: inline-flex; align-items: center; min-height: 32px; padding: 6px 12px; font: 600 12px/1 var(--font-mono); letter-spacing: 0.08em; color: var(--text); background: var(--bg-surface); border: 1px solid var(--line-strong); border-radius: 0; cursor: pointer; }
.seg:first-of-type .seg__label, .seg-btn:first-of-type { border-radius: 6px 0 0 6px; }
.seg:last-of-type .seg__label, .seg-btn:last-of-type { border-radius: 0 6px 6px 0; }
.seg + .seg .seg__label, .seg-btn + .seg-btn { margin-left: -1px; }
.seg:hover .seg__label, .seg-btn:hover { color: var(--accent); border-color: var(--accent); position: relative; }
/* The chosen segment is filled, not just recoloured: the change is in luminance and shape as
   well as hue, and the beat toggles also gain a tick. */
.seg input:checked + .seg__label, .seg-btn[aria-pressed="true"] { color: var(--bg-page); background: var(--accent); border-color: var(--accent); position: relative; }
.seg-btn[aria-pressed="true"]::before { content: "✓"; margin-right: 6px; }
.seg input:focus-visible + .seg__label { outline: 2px solid var(--accent); outline-offset: 2px; position: relative; }

/* KEY ONLY stands alone, so it is rounded on both ends whatever the :first-of-type rules say. */
.seg-btn.key-toggle { border-radius: 6px; }

/* FEEDS and TAG FILTERS: a <details> each, so they open and close without script and a screen
   reader announces them as expandable. hud.js only closes them (after a choice, an outside
   click or Escape) and keeps it to one open at a time. */
.dropdown > summary { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 6px 12px; list-style: none; cursor: pointer; font: 600 12px/1 var(--font-mono); letter-spacing: 0.08em; color: var(--text); background: var(--bg-surface); border: 1px solid var(--line-strong); border-radius: 6px; white-space: nowrap; }
.dropdown > summary::-webkit-details-marker { display: none; }
.dropdown > summary::after { content: "▾"; color: var(--text-muted); }
.dropdown > summary:hover { color: var(--accent); border-color: var(--accent); }
.dropdown[open] > summary { color: var(--accent); border-color: var(--accent); }
.dropdown[open] > summary::after { content: "▴"; color: var(--accent); }
/* What is chosen inside, on the closed summary: the feed's name, or how many tags are on. */
.dropdown__now { color: var(--accent); font-weight: 600; }
.dropdown__now:empty { display: none; }
.dropdown__now::before { content: "· "; color: var(--text-muted); }
.dropdown__panel, .filterbar__more > .facets { position: absolute; z-index: 95; padding: 14px 16px; background: var(--bg-surface); border: 1px solid var(--line-strong); border-radius: var(--radius); box-shadow: 0 16px 40px rgba(2, 6, 23, 0.6); overflow-y: auto; }
.filterbar__feeds { position: relative; }
.dropdown__panel { top: calc(100% + 6px); left: 0; width: max-content; min-width: 300px; max-width: min(420px, calc(100vw - 40px)); max-height: min(70vh, 560px); display: grid; gap: 10px; }
.dropdown__note { font-size: 12.5px; line-height: 1.5; color: var(--text-muted); max-width: 44ch; }
.filterbar__more { position: static; }
.filterbar__more > .facets { top: calc(100% - 2px); left: 20px; width: min(960px, calc(100% - 40px)); max-height: min(62vh, 560px); }

/* One row per feed: its name and its count, and under it, when the scope hides some of it, a
   plain underlined link to the same feed at ALL. The current feed gets a bar on its edge as
   well as a fill, so it does not rest on colour. */
.feed-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 2px; }
.feed-item { display: grid; }
.feed-link { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 7px 10px 7px 9px; border-left: 3px solid transparent; border-radius: 6px; color: var(--text); text-decoration: none; font: 500 12.5px/1.35 var(--font-mono); letter-spacing: 0.04em; }
.feed-link:hover { color: var(--accent); background: var(--bg-page); }
.feed-link[aria-current] { color: var(--text-head); background: var(--bg-page); border-left-color: var(--accent); font-weight: 600; }
.feed-link__label { min-width: 0; }
.feed-link__count { flex: none; min-width: 2.5em; text-align: right; font-size: 12px; color: var(--text-muted); }
.feed-link[aria-current] .feed-link__count { color: var(--accent); }
.feed-widen { justify-self: start; margin: -3px 0 5px 12px; padding: 0 2px; font-size: 11px; line-height: 1.5; letter-spacing: 0.04em; color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.feed-widen:hover { color: var(--text-head); }

.filterbar__actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.filterbar__count { font-size: 12.5px; color: var(--text-dim); }
/* On a phone both panels hang from the filter bar at its full width: a panel anchored to a
   summary halfway along a wrapped row would run off the side of the screen. */
@media (max-width: 720px) {
  .filterbar__feeds { position: static; }
  .dropdown__panel, .filterbar__more > .facets { top: calc(100% - 2px); left: 14px; right: 14px; width: auto; min-width: 0; max-width: none; max-height: 70vh; }
  .filterbar__actions { margin-left: 0; }
}

.facets { display: grid; gap: 6px; }
.facet > summary { cursor: pointer; font: 600 12px/1.8 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
.facet[open] > summary { color: var(--accent); }
.facet__chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 0 10px; }

/* ------------------------------------------------------------------ layout */

/* One column at the header's measure: nothing sits beside the view any more. */
.main { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); max-width: var(--measure); margin: 0 auto; padding: 20px; }
@media (max-width: 720px) { .main { padding: 16px; } }

/* ------------------------------------------------------------------- views */

.view { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); align-content: start; scroll-margin-top: calc(var(--header-h, 120px) + 8px); }
.view:focus { outline: none; }
.view__head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 14px; }
.view__title { font: 700 22px/1.25 var(--font-sans); letter-spacing: 0.04em; color: var(--text-head); }
.view__desc { color: var(--text-muted); font-size: 14px; line-height: 1.55; max-width: 90ch; }
.view__desc.mono { font-size: 12.5px; letter-spacing: 0.04em; }
.events-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-content: start; }

.panel { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-content: start; min-width: 0; padding: 16px; background: var(--bg-surface); border: 1px solid var(--line); border-radius: var(--radius); }
.panel__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px 12px; }
.panel__title { font: 600 12.5px/1.4 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-head); }
.panel-status { display: flex; align-items: center; gap: 8px; flex: none; }
.panel-count { font-size: 12.5px; color: var(--text-dim); }
/* A button is only as wide as its label: a grid item stretches by default. */
.panel > .btn, .events-body > .btn { justify-self: start; }

.dash-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: var(--gap); }
/* Source order is reading order: map, severity, pulse, top signals, then the two watch lists.
   On a wide screen they pair up as 8+4, 4+8 and 6+6; under 1120px every panel is full width. */
.dash-grid > .panel { grid-column: span 6; }
.dash-grid > .panel--map, .dash-grid > .panel--signals { grid-column: span 8; }
.dash-grid > .panel--severity, .dash-grid > .panel--pulse { grid-column: span 4; }
.dash-grid > .panel--brief { grid-column: 1 / -1; }
.brief__summary { margin: 0 0 var(--gap); max-width: 80ch; }
.brief__items { margin: 0 0 var(--gap); padding-left: 1.4em; }
.brief__items li { margin-bottom: 0.5em; }
.brief__why { display: block; color: var(--muted, inherit); }
.brief__gaps { margin: 0 0 var(--gap); padding-left: 1.4em; }
.panel--severity .gauges { grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); }
@media (max-width: 1120px) { .dash-grid > .panel, .dash-grid > .panel--map, .dash-grid > .panel--signals, .dash-grid > .panel--severity, .dash-grid > .panel--pulse { grid-column: 1 / -1; } }
.pulse-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); gap: var(--gap); align-items: start; }
.map-controls { display: flex; flex-wrap: wrap; align-items: end; gap: 10px 14px; }
.map-controls .field { flex: 1 1 280px; }

/* A block a link can jump to (#sec-trends). scroll-margin-top clears the sticky
   header, and it cannot be a constant: the header reflows as the viewport narrows and grows
   when the collection time arrives. initViews() measures it and publishes --header-h; the
   literal is only the no-JS fallback. */
.subsection { scroll-margin-top: calc(var(--header-h, 120px) + 8px); }
.subsection__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }

.led { width: 10px; height: 10px; border-radius: 50%; background: var(--text-muted); flex: none; }
.led[data-state="ok"] { background: var(--sev-low); }
.led[data-state="warn"] { background: var(--sev-medium); }
.led[data-state="bad"], .led[data-state="fail"] { background: var(--sev-critical); }
.led[data-state="idle"] { background: var(--text-muted); }

.empty, .hint, .notice { color: var(--text-dim); font-size: 14.5px; line-height: 1.6; max-width: 80ch; }
.hint { color: var(--text-muted); }
.notice { padding: 12px var(--gap); border: 1px solid var(--line-strong); border-radius: var(--radius); background: var(--bg-surface); }
.notice--widen { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; max-width: none; border-color: var(--accent); }
.load-error { border-color: var(--sev-critical); color: var(--text); }
.subhead { font: 600 12px/1.4 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-head); margin-top: 8px; }

/* -------------------------------------------------------------- severity */

/* One severity mapping shared by every component: badge, bars, gauge, radar blip, headline
   stripe, index band. --sev-color is the spec hue for fills and borders; --sev-text is what may
   be used as text on a surface or on the 10% tint; --sev-tint is the badge background. */
[data-severity="critical"] { --sev-color: var(--sev-critical); --sev-text: var(--sev-critical-text); --sev-tint: rgba(239, 68, 68, 0.1); }
[data-severity="high"] { --sev-color: var(--sev-high); --sev-text: var(--sev-high); --sev-tint: rgba(245, 158, 11, 0.1); }
[data-severity="medium"] { --sev-color: var(--sev-medium); --sev-text: var(--sev-medium); --sev-tint: rgba(251, 191, 36, 0.1); }
[data-severity="low"] { --sev-color: var(--sev-low); --sev-text: var(--sev-low); --sev-tint: rgba(16, 185, 129, 0.1); }
[data-severity="info"] { --sev-color: var(--sev-info); --sev-text: var(--sev-info); --sev-tint: rgba(56, 189, 248, 0.1); }
[data-severity="unknown"] { --sev-color: var(--text-muted); --sev-text: var(--text-muted); --sev-tint: rgba(148, 163, 184, 0.1); }
/* The AI desk's significance scale: the same badge, in the AI hue, so the two scales are never
   read as one. */
[data-significance] { --sev-color: var(--beat-ai); --sev-text: var(--beat-ai); --sev-tint: rgba(167, 139, 250, 0.1); }
.sig-badge { border-style: dashed; }
.sev-bars--three i { width: 6px; }
.facet-chip { min-height: 30px; }

.sev-badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px; border: 1px solid var(--sev-color, var(--text-muted)); border-radius: var(--radius-sm); background: var(--sev-tint, transparent); color: var(--sev-text, var(--text-muted)); font: 600 12px/1.1 var(--font-mono); letter-spacing: 0.04em; white-space: nowrap; }
.sev-badge__label { white-space: nowrap; }
.sev-shape { font-size: 13px; line-height: 1; }
.sev-bars { display: inline-flex; gap: 2px; }
.sev-bars i { display: block; width: 5px; height: 10px; border: 1px solid var(--sev-color, var(--text-muted)); }
.sev-bars i.on { background: var(--sev-color, var(--text-muted)); }

/* Importance is a third scale beside severity and AI significance, so it takes the accent rather
   than any severity hue, and its tier is the word: KEY filled, NOTABLE outlined, ROUTINE none. */
.imp-badge { display: inline-flex; align-items: center; padding: 4px 8px; border: 1px solid var(--accent); border-radius: var(--radius-sm); color: var(--accent); background: transparent; font: 700 12px/1.1 var(--font-mono); letter-spacing: 0.08em; white-space: nowrap; }
.imp-badge[data-tier="key"] { color: var(--bg-page); background: var(--accent); }
/* A margin, not the leading space in the text: a flex item drops that. */
.imp-badge__score { margin-left: 0.6ch; font-weight: 500; letter-spacing: 0.02em; }

/* ------------------------------------------------------------------ chips */

.chip { display: inline-flex; align-items: center; padding: 4px 8px; font: 500 12px/1.2 var(--font-mono); color: var(--text-dim); background: var(--bg-page); border: 1px solid var(--bg-surface); border-radius: var(--radius-sm); }
/* A chip you can press needs an edge you can see (3:1), unlike a label chip on a card. */
button.chip { cursor: pointer; border-color: var(--line-strong); }
button.chip:hover { border-color: var(--accent); color: var(--accent); }
button.chip[aria-pressed="true"] { border-color: var(--accent); color: var(--bg-page); background: var(--accent); }
button.chip[aria-pressed="true"]::before { content: "✓"; margin-right: 6px; }
.chip--cat { color: var(--accent); }
.chip--cve { color: var(--text-muted); }
.chip--kev { color: var(--sev-critical-text); border-color: var(--sev-critical); }
.chip--more { color: var(--text-muted); }
.chip--src { color: var(--text-muted); }
.chip--au { color: var(--text-head); border-color: var(--line-strong); }
.chip--ai { color: var(--ai-inference); border-color: var(--ai-inference); font-weight: 600; letter-spacing: 0.04em; }
.chip--beat { font-weight: 600; letter-spacing: 0.06em; border-color: var(--line-strong); color: var(--text); }
.chip--beat[data-beat="cyber"] { color: var(--accent); border-color: var(--accent); }
.chip--beat[data-beat="ai"], .chip--beat[data-beat="both"] { color: var(--beat-ai); border-color: var(--beat-ai); }
.chip--beat[data-beat="other"] { color: var(--text-muted); }

/* ------------------------------------------------------------- event card */

.event-list { list-style: none; margin: 0; padding: 0; content-visibility: auto; contain-intrinsic-size: auto 600px; }
.event-card { margin: 0 0 16px; padding: 16px; background: var(--bg-surface); border: 1px solid var(--line); border-radius: var(--radius); content-visibility: auto; contain-intrinsic-size: auto 190px; }
.event-card:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
.event-card__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 8px 12px; margin-bottom: 12px; font: 400 0.85rem/1.4 var(--font-mono); }
.event-card__time { color: var(--text-muted); }
.event-card__badges { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.event-card__body { font-family: var(--font-sans); }
.event-card__title { margin: 0 0 8px; font: 600 1.1rem/1.4 var(--font-sans); color: var(--text-head); }
.event-card__link { color: inherit; text-decoration: none; }
.event-card__link:hover, .event-card__link:focus-visible { color: var(--accent); text-decoration: underline; }
.event-card__summary { margin: 0 0 16px; font-size: 0.95rem; line-height: 1.5; color: var(--text-dim); }
.event-card__tags { display: flex; flex-wrap: wrap; gap: 8px; font: 400 0.75rem/1.2 var(--font-mono); }
.event-card__tags .chip { font-size: inherit; }
.event-more { margin-top: 12px; }
.event-more summary { cursor: pointer; color: var(--accent); font: 600 12px/1.6 var(--font-mono); letter-spacing: 0.08em; }
.event-more__body { display: grid; gap: 8px; margin-top: 8px; font-size: 14.5px; line-height: 1.6; color: var(--text-dim); }
.event-more__id { color: var(--text-muted); font-size: 12px; }
.event-more__body h4 { margin: 4px 0 0; font: 600 11.5px/1.4 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-head); }
.event-more__body ul { margin: 0; padding-left: 18px; }
.show-more { justify-self: start; }

/* ----------------------------------------------------- dashboard lists */

/* Top signals: a static list, one headline per row. */
.headline-list, .watch-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.headline, .watch { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 6px 12px; align-items: center; padding: 8px 12px; background: var(--bg-page); border: 1px solid var(--line); border-left: 3px solid var(--sev-color, var(--text-muted)); border-radius: 6px; }
.headline[data-beat="ai"] { border-left-color: var(--beat-ai); }
.headline__link, .watch__link { color: var(--text); text-decoration: none; font-size: 15px; line-height: 1.45; }
.headline__link:hover, .headline__link:focus-visible, .watch__link:hover, .watch__link:focus-visible { color: var(--accent); text-decoration: underline; }
.watch { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.watch .sev-badge { justify-self: start; }
.watch__link { font-size: 14.5px; }
.watch__all { justify-self: start; margin-top: 10px; }
.headline__badges { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
@media (max-width: 560px) { .headline { grid-template-columns: minmax(0, 1fr); } .headline .headline__badges { justify-self: start; } }

/* ------------------------------------------------------- index, gauges, radar */

.cp-index { display: grid; gap: 8px; padding: 14px; border-radius: 6px; background: var(--bg-page); border: 1px solid var(--line); }
.cp-index__label { font: 600 11.5px/1.4 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }
.cp-index__value { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.cp-index__num { font: 600 40px/1 var(--font-mono); letter-spacing: -0.03em; color: var(--accent); font-variant-numeric: tabular-nums; }
.cp-index__band { font: 600 13px/1.2 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--sev-text, var(--text)); }
.cp-index__note { font-size: 12.5px; line-height: 1.55; color: var(--text-muted); }
.ekg { width: 100%; height: 56px; display: block; }
.ekg-base { fill: none; stroke: var(--line-strong); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.ekg-pulse { fill: none; stroke: var(--accent); stroke-width: 2.5; vector-effect: non-scaling-stroke; stroke-dasharray: 8 92; stroke-dashoffset: 0; }

.gauges { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; }
.gauge { display: grid; justify-items: center; gap: 6px; padding: 12px 8px; background: var(--bg-page); border: 1px solid var(--line); border-radius: 6px; }
.gauge svg { width: 100px; height: 100px; }
.gauge-track { fill: none; stroke: var(--line); stroke-width: 8; stroke-linecap: butt; }
.gauge-arc { fill: none; stroke: var(--sev-color, var(--text-muted)); stroke-width: 8; stroke-linecap: butt; }
.gauge-num { font: 600 23px var(--font-mono); fill: var(--text-head); text-anchor: middle; }
.gauge__label { display: flex; align-items: center; gap: 6px; }

.radar { position: relative; width: 100%; max-width: 280px; aspect-ratio: 1; margin: 6px auto 0; border-radius: 50%; overflow: hidden; background: var(--bg-page); border: 1px solid var(--line); }
.radar svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.radar-ring { fill: none; stroke: var(--line-strong); stroke-width: 0.6; }
.radar-axis { stroke: var(--line); stroke-width: 0.6; }
.radar-blip { fill: var(--sev-color, var(--text-muted)); stroke: var(--bg-page); stroke-width: 0.5; }
.radar-au-ring { fill: none; stroke: var(--accent); stroke-width: 0.8; }
.radar-sweep { position: absolute; inset: 0; border-radius: 50%; background: conic-gradient(from 0deg, transparent 0deg 300deg, rgba(6, 182, 212, 0.22) 360deg); opacity: 0.5; will-change: transform; }
.radar-legend { font-size: 12.5px; color: var(--text-muted); text-align: center; margin-top: 8px; }

/* ------------------------------------------------------------- world map */

/* Choropleth by event count: the cyan family is count, never severity. Every tier ascends in
   luminance from "none", so the legend reads in one direction (asserted in the tests). */
.map-host { display: grid; gap: 10px; }
.map-svg { width: 100%; height: auto; display: block; background: var(--bg-page); border: 1px solid var(--line); border-radius: 6px; }
.map-country, .map-legend__sw { --map-none: #334155; }
.map-country { fill: var(--map-none); stroke: var(--bg-page); stroke-width: 0.6; }
.map-country[data-load="1"], .map-legend__sw[data-load="1"] { --map-load: #155E75; }
.map-country[data-load="2"], .map-legend__sw[data-load="2"] { --map-load: #0E7490; }
.map-country[data-load="3"], .map-legend__sw[data-load="3"] { --map-load: #0891B2; }
.map-country[data-load="4"], .map-legend__sw[data-load="4"] { --map-load: var(--accent); }
.map-country[data-load] { fill: var(--map-load); cursor: pointer; }
.map-country[data-load]:hover { stroke: var(--text-head); stroke-width: 1.2; }
.map-country.is-selected { fill: var(--text-head); stroke: var(--bg-page); stroke-width: 1.2; }
.map-country:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; stroke: var(--text-head); stroke-width: 2; }
.map-legend { display: flex; flex-wrap: wrap; gap: 8px 18px; font: 500 12px/1.4 var(--font-mono); letter-spacing: 0.04em; color: var(--text-dim); }
.map-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.map-legend__sw { display: inline-block; width: 14px; height: 14px; border-radius: 3px; background: var(--map-load, var(--map-none)); border: 1px solid var(--line-strong); }

.field { display: grid; gap: 4px; }
.field__label { font: 600 11px/1.5 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); }
.input { font: 400 15px var(--font-sans); color: var(--text); background: var(--bg-page); border: 1px solid var(--line-strong); border-radius: 6px; padding: 8px 12px; min-height: 38px; width: 100%; max-width: 520px; }

/* ------------------------------------------------------------- trends */

/* The tables scroll sideways on a narrow screen rather than stacking: every cell is a figure,
   and a figure without its column heading means nothing. */
#trends { display: grid; gap: 12px; }
.trend-chart { display: block; width: 100%; max-width: 560px; height: auto; }
.trend-day__bar { fill: var(--accent); }
/* Part of a day collected is an outline, not a paler fill: a shape difference survives
   greyscale and colour-blindness, and the legend says so in words. */
.trend-day[data-coverage="partial"] .trend-day__bar { fill: none; stroke: var(--accent); stroke-width: 1.5; stroke-dasharray: 3 2; }
.trend-day__gap { stroke: var(--text-muted); stroke-width: 1.5; }
.trend-day__num, .trend-day__date { font: 500 8px/1 var(--font-mono); text-anchor: middle; fill: var(--text-dim); }
.trend-day__date { fill: var(--text-muted); }
.trend-legend { font: 500 12px/1.5 var(--font-mono); color: var(--text-muted); margin: 0; }
.trend-days > summary { cursor: pointer; font: 600 12px/1.6 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); }
.trend-days[open] > summary { color: var(--accent); }
.trend-block { display: grid; gap: 10px; justify-items: start; }
.trend-scroll { width: 100%; overflow-x: auto; }
.trend-table { width: 100%; border-collapse: collapse; font-size: 14px; text-align: left; }
.trend-caption { caption-side: top; text-align: left; padding: 0 0 8px; font-size: 13px; color: var(--text-muted); }
.trend-table th, .trend-table td { padding: 8px 14px 8px 0; vertical-align: top; border-bottom: 1px solid var(--line); }
.trend-table thead th { font: 600 11px/1.4 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); border-bottom-color: var(--line-strong); white-space: nowrap; }
.trend-table td { color: var(--text-dim); }
.trend-table tbody th { font-weight: 600; color: var(--text-head); min-width: 120px; }
.trend-topic { display: block; }
.trend-kind { display: block; font-size: 11px; font-weight: 400; letter-spacing: 0.06em; color: var(--text-muted); }
.trend-state { display: inline-flex; align-items: center; gap: 6px; font: 600 12px/1 var(--font-mono); letter-spacing: 0.04em; white-space: nowrap; color: var(--text-dim); }
.trend-state[data-trend="new"], .trend-state[data-trend="rising"] { color: var(--sev-high); }
.trend-state[data-trend="falling"] { color: var(--sev-low); }
.trend-events { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; min-width: 220px; }

/* ------------------------------------------------------- crew and system */

.crew-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); gap: 12px; content-visibility: auto; contain-intrinsic-size: auto 400px; }
.crew-card { background: var(--bg-page); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; display: grid; gap: 9px; align-content: start; }
/* A bot takes its accent from whatever is carrying it, so the same agent's eyes are the same
   colour on its card and in its run-table row. Declared on both carriers rather than as a
   default on .bot itself: a value set on the element would beat the inherited one. */
.crew-card, .run-table tbody tr { --bot-accent: var(--accent); }
.crew-card[data-desk="ENGINEERING"], .run-table tbody tr[data-desk="ENGINEERING"] { --bot-accent: var(--sev-medium); }
.crew-card[data-desk="OPERATIONS"], .run-table tbody tr[data-desk="OPERATIONS"] { --bot-accent: var(--sev-low); }
.crew-card__head { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 2px 12px; align-items: center; }
.crew-card__id { display: grid; gap: 2px; min-width: 0; }
.crew-card__callsign { font: 600 15px/1.2 var(--font-mono); letter-spacing: 0.06em; color: var(--text-head); min-width: 0; }
.crew-card__desk { font: 500 11px/1.3 var(--font-mono); letter-spacing: 0.06em; color: var(--text-muted); }
.crew-card__status { display: flex; align-items: center; gap: 6px; font: 500 12px/1.3 var(--font-mono); color: var(--text-dim); }
.crew-card__beat { color: var(--text-dim); font-size: 14.5px; line-height: 1.5; }
.crew-card__quote { color: var(--text-dim); font-size: 13.5px; font-style: italic; border-left: 2px solid var(--line-strong); padding-left: 10px; }
.kv.crew-card__stats { font-size: 13px; grid-template-columns: max-content minmax(0, 1fr); gap: 5px 14px; }
.crew-card__idle { color: var(--text-muted); font-size: 13.5px; }
.crew-card__runtime { justify-self: start; }

/* The face. One shared body, one accessory per agent, eyes tinted by desk. */
.bot { grid-row: span 2; width: 62px; height: 62px; display: block; margin: -4px -2px -4px -6px; }
.bot--sm { grid-row: auto; width: 32px; height: 32px; margin: 0; flex: none; }
.bot-shell { fill: var(--text-dim); stroke: var(--text-head); stroke-width: 1; }
.bot-visor { fill: var(--bg-page); stroke: none; }
.bot-eye { fill: var(--bot-accent); }
.bot-line { fill: none; stroke: var(--bot-accent); stroke-width: 1.6; stroke-linecap: round; }
.bot-dim { fill: none; stroke: var(--text-muted); stroke-width: 1.4; stroke-linecap: round; }
.bot-fill { fill: var(--text-muted); }
/* Detail cut into a filled shape rather than drawn on top of it, in the page colour behind. */
.bot-etch { fill: none; stroke: var(--bg-page); stroke-width: 1.2; stroke-linecap: round; }
.bot-glint { fill: none; stroke: var(--text-dim); stroke-width: 1.2; stroke-linecap: round; opacity: 0.5; }
.bot-mouth { fill: none; stroke: var(--bg-page); stroke-width: 1.4; stroke-linecap: round; }
.crew-card[data-status="active"] .bot-shell { stroke: var(--sev-low); stroke-width: 1.6; }
.crew-card[data-status="degraded"] .bot-shell { stroke: var(--sev-medium); stroke-width: 1.6; }

/* The personas retired on 31 October 2026: one line each, the callsign and why. */
.crew-retired { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; max-width: 70ch; color: var(--text-dim); font-size: 14px; line-height: 1.5; }
.crew-retired__callsign { font-weight: 600; letter-spacing: 0.06em; color: var(--text-muted); }

/* The SYSTEM explainer is prose, so it gets a measure. */
.explain { display: grid; gap: 12px; max-width: 70ch; color: var(--text-dim); font-size: 15.5px; line-height: 1.7; }
.explain strong { color: var(--text-head); font-weight: 600; }

/* Who did what in the last run: a table, because the question is a comparison down a column. */
.run-table { width: 100%; border-collapse: collapse; font-size: 14px; text-align: left; }
.run-table th, .run-table td { padding: 10px 14px 10px 0; vertical-align: top; border-bottom: 1px solid var(--line); }
.run-table thead th { font: 600 11px/1.4 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); border-bottom-color: var(--line-strong); white-space: nowrap; }
.run-table tbody th { font-weight: 400; min-width: 148px; }
.run-table__who { display: flex; align-items: center; gap: 9px; }
.run-table__id { display: grid; gap: 2px; min-width: 0; }
.run-table__callsign { font: 600 13.5px/1.3 var(--font-mono); letter-spacing: 0.04em; color: var(--text-head); }
.run-table__desk { font: 500 11px/1.3 var(--font-mono); color: var(--text-muted); }
.run-table td { color: var(--text-dim); }
.run-table__did { color: var(--text); }
.run-table__did--none { color: var(--text-muted); }
@media (max-width: 720px) {
  .run-table, .run-table tbody, .run-table tr, .run-table td, .run-table tbody th { display: block; }
  .run-table thead { display: none; }
  .run-table tr { border-bottom: 1px solid var(--line); padding: 10px 0; }
  .run-table th, .run-table td { border: 0; padding: 2px 0; }
}

/* Pipeline replay */
.pipe-svg { width: 100%; height: 40px; display: block; }
.pipe-trace { fill: none; stroke: var(--line-strong); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.pipe-pulse { fill: none; stroke: var(--accent); stroke-width: 2.5; vector-effect: non-scaling-stroke; stroke-dasharray: 6 94; stroke-dashoffset: 0; }
.pipe-nodes { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 6px; }
.pipe-node { background: var(--bg-page); border: 1px solid var(--line); border-radius: 6px; padding: 9px 6px; text-align: center; display: grid; gap: 4px; justify-items: center; }
.pipe-node__name { font: 600 11px/1.25 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-head); }
.pipe-node__count { font-size: 18px; color: var(--accent); }
.pipe-node__state { font-size: 12px; color: var(--text-dim); }
@media (max-width: 760px) { .pipe-nodes { grid-template-columns: repeat(2, minmax(0, 1fr)); } .pipe-svg { display: none; } }

.source-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 230px), 1fr)); gap: 6px; content-visibility: auto; contain-intrinsic-size: auto 400px; }
.source-node { display: flex; gap: 8px; align-items: center; padding: 8px 11px; border-radius: 6px; background: var(--bg-page); border: 1px solid var(--line); font-size: 14px; }
.source-node .sev-shape { color: var(--st-color, var(--text-muted)); }
.source-node__name { flex: 1; min-width: 0; font-family: var(--font-mono); font-size: 12.5px; }
.source-node__state { font: 600 11px/1 var(--font-mono); letter-spacing: 0.06em; color: var(--st-text, var(--text-muted)); white-space: nowrap; }
.source-node[data-health="healthy"] { --st-color: var(--sev-low); --st-text: var(--sev-low); border-left: 3px solid var(--sev-low); }
.source-node[data-health="degraded"] { --st-color: var(--sev-medium); --st-text: var(--sev-medium); border-left: 3px solid var(--sev-medium); }
.source-node[data-health="broken"] { --st-color: var(--sev-critical); --st-text: var(--sev-critical-text); border-left: 3px solid var(--sev-critical); }
.source-node[data-health="off"] { --st-color: var(--text-muted); --st-text: var(--text-muted); border-left: 3px solid var(--text-muted); }
/* The label column may wrap so long labels take two lines instead of crushing the value. A
   label whose longest word is wider than the column carries a soft hyphen (hud.js). */
.kv { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); gap: 6px 16px; margin: 0; font-size: 13.5px; }
.kv dt { color: var(--text-muted); min-width: 0; overflow-wrap: break-word; }
.kv dd { margin: 0; color: var(--text); min-width: 0; overflow-wrap: break-word; }

.site-footer { max-width: 1480px; margin: 0 auto; padding: 28px 20px 40px; color: var(--text-muted); font-size: 13.5px; line-height: 1.6; }
.site-footer p { max-width: 90ch; }

/* Long lists are skipped by layout until they scroll near the viewport. */
.events-body, .panel--signals { content-visibility: auto; contain-intrinsic-size: auto 300px; }
/* Canvas particle field: fixed behind the page, paused by hud.js (30 fps, DPR <= 2,
   visibilitychange, IntersectionObserver) and hidden outright under FX OFF. */
.fx-field { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; }
.fx-off .fx-field { display: none; }

/* -------------------------------------------- event and history pages */

/* Their header has the same tabs, as links back into the dashboard, and only the one row. */
main.wrap { padding-top: 20px; }
main.wrap > * + * { margin-top: var(--gap); }

.hud-panel { position: relative; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-surface); min-width: 0; }
.hud-panel__inner { position: relative; border-radius: inherit; height: 100%; }
.hud-panel__head { display: flex; align-items: center; gap: 12px; padding: 14px 18px 10px; border-bottom: 1px solid var(--line); }
.panel-title { flex: 1; min-width: 0; font: 600 13px/1.3 var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-head); }
.panel-body { padding: 16px 18px 20px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-content: start; }
.panel-body > .btn { justify-self: start; }

.event-detail { display: grid; gap: 16px; }
.detail { display: grid; gap: 16px; }
.detail__title { margin: 0; font: 700 clamp(22px, 3.2vw, 30px)/1.3 var(--font-sans); color: var(--text-head); overflow-wrap: normal; }
.detail__meta { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; font-size: 13px; }
.detail__id { color: var(--text-muted); font-size: 12.5px; }
.detail__summary { color: var(--text-dim); font-size: 16.5px; line-height: 1.65; max-width: 76ch; }
.detail__why { border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: 6px; padding: 10px 14px; background: var(--bg-surface); color: var(--text); }
.detail__why strong { color: var(--accent); font: 600 11.5px/1.6 var(--font-mono); letter-spacing: 0.06em; }
.detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: var(--gap); }
.detail-block { background: var(--bg-surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; display: grid; gap: 10px; align-content: start; min-width: 0; }
.detail-label { margin: 0; font: 600 11px/1.4 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); }
.detail-list ul { margin: 0; padding-left: 18px; display: grid; gap: 6px; color: var(--text-dim); font-size: 14.5px; line-height: 1.6; }
.detail-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); gap: var(--gap); align-items: start; }
.detail-col { display: grid; gap: 10px; align-content: start; padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-surface); min-width: 0; }
.detail-col--ev { border-top: 4px solid var(--accent); }
.detail-col--ai { border-top: 4px solid var(--ai-inference); background-image: repeating-linear-gradient(45deg, rgba(244, 114, 182, 0.04) 0 10px, transparent 10px 20px); }
.detail-col__head { margin: 0; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font: 600 13px/1.3 var(--font-mono); letter-spacing: 0.06em; text-transform: uppercase; }
.detail-col--ev .detail-col__head { color: var(--accent); }
.detail-col--ai .detail-col__head { color: var(--ai-inference); }
.detail-note { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: 13px; color: var(--text); }
.source-reports, .claims, .cve-list, .mitre-list, .related-events { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.source-report { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline; font-size: 14px; }
.source-report__lineage, .cve-row__vector, .mitre-row__set { color: var(--text-muted); font-size: 12px; }
.claim { display: grid; gap: 6px; background: var(--bg-page); border: 1px solid var(--line); border-radius: 6px; padding: 9px 11px; font-size: 14px; }
.claim__text { color: var(--text); }
.claim__ev { font-size: 12px; color: var(--text-muted); }
.cve-row, .mitre-row { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; background: var(--bg-page); border: 1px solid var(--line); border-radius: 6px; padding: 9px 11px; font-size: 14px; }
.cve-row__vector { flex-basis: 100%; }
.advisory-list { flex-basis: 100%; display: grid; gap: 4px; }
.advisory-row { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: center; font-size: 12px; }
.advisory-row__pkg { color: var(--text-muted); }
.mitre-row__id { color: var(--accent); }
.related-events li { display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline; font-size: 14px; }
.related-events li a { flex-basis: 100%; }
.related-title { color: var(--text-dim); }

/* Timeline: tick-marked axis, hex nodes, Sydney day groups */
.timeline { display: grid; gap: 10px; background: var(--bg-surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; }
.timeline__day { display: grid; gap: 8px; }
.timeline__dayhead { margin: 0; font: 600 11.5px/1.4 var(--font-mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-dim); }
.timeline__list { list-style: none; margin: 0; padding: 0 0 0 20px; border-left: 2px solid var(--line-strong); display: grid; gap: 8px; }
.timeline__node { position: relative; display: grid; grid-template-columns: max-content max-content 1fr; gap: 4px 10px; align-items: baseline; background: var(--bg-page); border: 1px solid var(--line); border-radius: 6px; padding: 9px 11px; font-size: 14px; }
.timeline__node::before { content: ""; position: absolute; left: -12px; top: 15px; width: 12px; height: 2px; background: var(--line-strong); }
.timeline__hex { position: absolute; left: -29px; top: 9px; width: 16px; height: 16px; background: var(--accent); clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%); }
.timeline__node time { color: var(--text-muted); font: 400 12.5px/1.4 var(--font-mono); }
.timeline__summary { color: var(--text); }
.timeline__src { grid-column: 1 / -1; color: var(--text-muted); font-size: 12.5px; }

/* History navigation */
.history-nav { display: flex; flex-wrap: wrap; gap: 10px 14px; align-items: center; justify-content: space-between; }
.history-nav__now { font: 600 16px/1.2 var(--font-mono); color: var(--accent); }
.history-nav__end { color: var(--text-muted); border-style: dashed; cursor: default; }
.history-day__head { display: grid; gap: 4px; margin-top: 6px; }

/* Motion. Every loop lives in a no-preference query with one rule per keyframes. */
@keyframes radar-sweep {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
@keyframes ekg-travel {
  from { stroke-dashoffset: 0; }
  to { stroke-dashoffset: -100; }
}
@keyframes trace-flow {
  from { stroke-dashoffset: 0; }
  to { stroke-dashoffset: -100; }
}
@media (prefers-reduced-motion: no-preference) {
  .radar-sweep { animation: radar-sweep 14s linear infinite; }
}
@media (prefers-reduced-motion: no-preference) {
  .ekg-pulse { animation: ekg-travel 4s linear infinite; }
}
@media (prefers-reduced-motion: no-preference) {
  .pipe-pulse { animation: trace-flow 3s linear infinite; }
}
/* FX OFF escape hatch: stops every loop on the page */
.fx-off *, .fx-off *::before, .fx-off *::after { animation: none !important; transition: none !important; }

@media (max-width: 560px) {
  .panel, .event-card { padding: 14px; }
  .hud-panel__head { padding: 12px 14px 8px; flex-wrap: wrap; }
  .panel-body { padding: 14px 14px 18px; }
  .timeline__node { grid-template-columns: 1fr; }
  .detail__title { overflow-wrap: anywhere; }
  .site-header__row, .filterbar { padding-left: 14px; padding-right: 14px; }
  .tabs { margin: 0 -14px; padding: 0 14px; }
}
/* On paper the navigation is noise: the view printed is the one open, and its head says which. */
@media print {
  .tabs, .site-header__controls, .filterbar, .fx-field { display: none; }
  .site-header { position: static; }
  .site-header__row { grid-template-columns: max-content minmax(0, 1fr); grid-template-areas: "brand status"; }
  .main { max-width: none; padding: 12px 0; }
}
