/* Global News Dashboard — dark-first theme.
   Color tokens follow the validated reference palette:
   surfaces #0d0d0d/#1a1a19 (dark) and #f9f9f7/#fcfcfb (light);
   status colors (importance) are fixed and never reused as series colors. */

/* D.E.L.P.H.I. theme: green primary, gold brand accents on near-black.
   Contrast computed against surfaces (all pairs ≥3.8:1; primary ≥5:1).
   Status colors (importance tiers) stay fixed and are never themed. */
:root, :root[data-theme="dark"] {
  /* Zero rather than deleted: the board, the map and the right-hand decorative
     column all measure themselves against what the action rail used to
     reserve. The rail is gone; one token says so in one place. */
  --rail-w: 0px;
  /* A feed column's natural width, and how wide it may grow to use up a wide
     monitor. 360 is the width the board was built around; 560 is where a
     headline stops scanning like a column and starts scanning like a page. */
  --col-base: 360px;
  --col-grow-max: 560px;
  /* Width of a decorative Greek column. The right-hand one sits inboard of the
     rail, so the board has to clear both — see the board's padding. */
  --pillar-w: 58px;
  --page: #0c0e0c;
  --surface: #151815;
  --surface-2: #1e231e;
  --ink: #ffffff;
  --ink-2: #c2c6bc;
  --ink-muted: #8a8d82;
  --hairline: #2b312b;
  --border: rgba(255, 255, 255, 0.10);
  --accent: #4caf50;
  --accent-strong: #2e7d32;
  --accent-ink: #ffffff;
  --gold: #d4af37;
  --gold-soft: rgba(212, 175, 55, 0.34);
  --critical: #d03b3b;
  --serious: #ec835a;
  --warning: #fab219;
  --good: #0ca30c;
}
:root[data-theme="light"] {
  --page: #f7f7f1;
  --surface: #fcfcf8;
  --surface-2: #eeeee2;
  --ink: #0b0b0b;
  --ink-2: #504f47;
  --ink-muted: #86847a;
  --hairline: #ddddcf;
  --border: rgba(11, 11, 11, 0.10);
  --accent: #2e7d32;
  --accent-strong: #2e7d32;
  --accent-ink: #ffffff;
  --gold: #9c7c1c;
  --gold-soft: rgba(156, 124, 28, 0.40);
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  background: var(--page);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px;
  line-height: 1.45;
  /* The window is the frame: header takes what it needs, the board takes the
     rest. Nothing here may depend on a hard-coded header height — the header
     wraps to more rows on narrow windows, and a guessed offset made the board
     overflow the viewport, which pushed its horizontal scrollbar below the
     fold exactly when there were enough feeds to need it. */
  height: 100%;
  display: flex;
  flex-direction: column;
  /* The board scrolls horizontally within itself; the page never should. */
  overflow: hidden;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; }
/* Visible keyboard focus for accessibility (mouse users unaffected). */
:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---------- top bar ---------- */
.topbar {
  position: relative;   /* the ::after meander frieze positions against this */
  z-index: 500;
  display: flex; align-items: center; gap: 16px;
  padding: 10px 16px;
  /* Room for the collapsed rail so the sign-in button can't sit under it. */
  padding-right: calc(var(--rail-w) + 16px);
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
  flex-wrap: wrap;
  flex: 0 0 auto;   /* takes its natural height; the board absorbs the rest */
}
.brand {
  font-size: 17px; font-weight: 700; white-space: nowrap;
  display: flex; align-items: center; gap: 9px;
  color: var(--gold);
}
.brand span { letter-spacing: 1.5px; font-family: Georgia, "Times New Roman", serif; }
/* The artwork already includes the wordmark — cropping it into a circle cut
   that off and the adjacent text repeated it. Shown whole instead. */
.brand-logo {
  /* Tall enough for the wordmark inside the artwork to read. */
  height: 46px; width: auto;
  object-fit: contain;
  display: block;
}

/* Four numbers in a 400px panel rather than across a header: they wrap into a
   grid instead of squeezing until "1187/5120" reads as "11875/12". */
.stat-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.stat-tile {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 6px 10px;
  min-width: 0;
  text-align: center;
}
.stat-value { font-size: 18px; font-weight: 700; overflow-wrap: anywhere; }
.stat-label { font-size: 10.5px; color: var(--ink-muted); }

.view-switch {
  display: flex; gap: 0;
  border: 1px solid var(--border); border-radius: 9px; overflow: hidden;
}
.view-switch button {
  background: var(--surface-2); color: var(--ink-2);
  border: none; padding: 7px 14px; cursor: pointer; font-size: 13px;
}
.view-switch button.active { background: var(--accent-strong); color: var(--accent-ink); font-weight: 600; }
/* The unseen-alert count rides on the Atlas tab, since that is where the
   alerts now are. Inline rather than pinned to the corner: the switch clips
   its overflow to keep the joined-up look, so a badge hanging outside the
   button would be sliced in half. */
.view-switch .bell-count {
  position: static;
  margin-left: 7px;
  padding: 1px 6px;
  vertical-align: 1px;
}

/* Ends with help, then the account itself in the corner, which is where every
   application has taught people to look — and where Settings now opens from,
   since both are about the same person. */
.topbar-actions { display: flex; gap: 8px; align-items: center; margin-left: auto; }
.topbar-actions #btn-help,
.topbar-actions #btn-profile { font-size: 16px; line-height: 1; padding: 6px 10px; }

/* The Pantheons board.
   Two columns with a hairline between them: on the left the two ways in — make
   one, or join one somebody else made — and on the right the ones you are
   already in. */
.pantheons-view { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-top: 12px; }
.pantheons-body {
  display: flex;
  gap: 0;
  align-items: stretch;
  /* Against the left wall, not centred. A centred 1400px band put the left
     column a third of the way across a wide screen, with the hairline out in
     the middle of the page and the tiles crowded into the last third. */
  padding: 0 16px 30px;
  min-height: 100%;
}
/* Clear the decorative columns, which are fixed and would sit over this. They
   are hidden below 1281px, and so is the room kept for them. */
@media (min-width: 1281px) {
  .pantheons-body { padding: 0 calc(var(--pillar-w) + 16px) 30px; }
}
.pn-side {
  /* About a fifth of the width: a button and a list of names does not need
     more. Clamped at both ends so the list stays readable in a narrow window
     and does not sprawl across a wide one. */
  flex: 0 0 20%;
  min-width: 240px;
  max-width: 380px;
  padding: 4px 20px 20px 0;
  border-right: 1px solid var(--hairline);
}
/* Everything left over is the wall of joined Pantheons. */
.pn-main { flex: 1 1 auto; min-width: 0; padding: 4px 0 20px 24px; }
.pn-new { width: 100%; margin-bottom: 10px; }
.pantheons-view .set-h { padding: 0; }

/* One joined Pantheon, as a button carrying what you need to know first. */
.pn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}
.pn-tile {
  position: relative;
  display: flex; flex-direction: column; gap: 6px;
  text-align: left;
  padding: 12px 14px 40px;
  background: var(--surface-2);
  color: var(--ink);
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
}
.pn-tile:hover { border-color: var(--gold-soft); }
.pn-tile-head { display: flex; align-items: baseline; gap: 8px; }
.pn-tile-name { font-weight: 600; font-size: 15px; flex: 1 1 auto; min-width: 0; }
.pn-tile-role {
  flex: 0 0 auto; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.5px; color: var(--ink-muted);
}
.pn-tile-desc {
  margin: 0; font-size: 13px; color: var(--ink-2);
  /* Two lines, so a long description cannot make one tile twice the height of
     its neighbour. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.pn-tile-quiet { color: var(--ink-muted); font-style: italic; }
.pn-tile-facts { display: flex; flex-wrap: wrap; gap: 4px 12px; font-size: 12px; color: var(--ink-muted); }
.pn-tile-manage { position: absolute; right: 12px; bottom: 10px; }
#global-search {
  width: 300px; padding: 7px 10px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px;
}
#lang-select {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 8px; max-width: 150px;
}

.btn {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 5px;
  padding: 7px 12px; cursor: pointer;
}
.btn:hover { border-color: var(--ink-muted); }
.btn-primary { background: var(--accent-strong); color: var(--accent-ink); border-color: transparent; }
.btn.small { padding: 4px 9px; font-size: 12.5px; }
.btn.danger { color: var(--critical); border-color: var(--critical); background: transparent; }
/* One size for every icon-only button, so rows of them line up instead of
   each glyph setting its own width. */
.icon-btn {
  background: none; border: none; color: var(--ink-2);
  font-size: 15px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border-radius: 5px;
}
.icon-btn:hover { background: var(--surface-2); }

.bell { position: relative; }
.bell-count {
  position: absolute; top: -6px; right: -6px;
  background: var(--critical); color: #fff;
  font-size: 10.5px; font-weight: 700;
  border-radius: 9px; padding: 1px 5px;
}

.topbar::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -11px;
  height: 11px;
  background: url("../img/meander-dark.svg") repeat-x center / 22px 11px;
  pointer-events: none;
}
:root[data-theme="light"] .topbar::after { background-image: url("../img/meander-light.svg"); }
.gate-card::before {
  content: "";
  display: block;
  height: 12px; margin: -10px -30px 10px;
  background: url("../img/meander-dark.svg") repeat-x center / 24px 12px;
  border-bottom: 1px solid var(--gold-soft);
}
:root[data-theme="light"] .gate-card::before { background-image: url("../img/meander-light.svg"); }

/* ---------- Greek columns framing the board (SVG artwork) ---------- */
.pillar {
  position: fixed; top: 57px; bottom: 0; width: var(--pillar-w);
  z-index: 1; pointer-events: none;
  display: flex; flex-direction: column;
  filter: drop-shadow(0 0 10px rgba(0, 0, 0, 0.45));
}
.pillar-left { left: 0; }
.pillar-right { right: var(--rail-w); transform: scaleX(-1); }  /* mirror: light from center */
.pillar-cap {
  flex: 0 0 50px;
  background: url("../img/column-cap-dark.svg") no-repeat center / 100% 100%;
}
.pillar-shaft {
  flex: 1;
  background: url("../img/column-shaft-dark.svg") repeat-y center / 100% auto;
}
.pillar-base {
  flex: 0 0 44px;
  background: url("../img/column-base-dark.svg") no-repeat center / 100% 100%;
}
:root[data-theme="light"] .pillar-cap { background-image: url("../img/column-cap-light.svg"); }
:root[data-theme="light"] .pillar-shaft { background-image: url("../img/column-shaft-light.svg"); }
:root[data-theme="light"] .pillar-base { background-image: url("../img/column-base-light.svg"); }
@media (max-width: 1280px) { .pillar { display: none; } }

/* Greek display face for headings */
.feed-head h3, .side-panel-head h2, .modal-head h2, .empty-state h2,
.wiz-summary-name, #ev-title {
  font-family: Georgia, "Times New Roman", serif;
  letter-spacing: 0.6px;
}

/* The strip above a Pantheon's columns: where you are, and the way back. */
.board-header {
  display: flex; align-items: center; gap: 12px; flex: 0 0 auto;
  padding: 10px calc(var(--pillar-w) + 16px);
  border-bottom: 1px solid var(--hairline);
  background: var(--surface);
}
.board-header-name { font-weight: 600; font-size: 15px; }
.board-header .s-meta {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------- board ---------- */
/* Single lateral rail: feeds never wrap below one another. */
.board {
  display: flex;
  flex-wrap: nowrap;
  gap: 14px;
  padding: 22px 16px 14px;
  align-items: flex-start;
  overflow-x: auto;
  /* Vertical scrolling belongs to each column, so the board's own horizontal
     bar stays pinned to the bottom of the visible area. */
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  /* When the columns cannot fill the board even at full width, split what is
     left between both sides instead of leaving it all on the right. `safe`
     matters: plain `center` makes the first column unreachable once the board
     overflows, because the overflow goes off the start edge. */
  justify-content: safe center;
  /* Fill the space the header leaves, whatever that turns out to be. min-height
     0 is required or flex refuses to shrink this below its content. */
  flex: 1 1 auto;
  min-height: 0;
  /* Keep the last column clear of the collapsed action rail. */
  padding-right: calc(var(--rail-w) + 16px);
  /* The native bar is hidden in favour of .board-scroll below. Most platforms
     now draw overlay scrollbars, which occupy no space and fade out when idle
     — they cannot be made permanently visible, and the lateral bar is the only
     indication that more feeds exist off-screen. */
  scrollbar-width: none;
}
.board::-webkit-scrollbar { height: 0; width: 0; }

/* Always-present horizontal scrollbar for the board. Sits in the layout below
   the board, so it is on screen at every window height, and it appears only
   when there is actually something to scroll to. */
.board-scroll {
  flex: 0 0 auto;
  height: 14px;
  margin: 0 16px 8px;
  margin-right: calc(var(--rail-w) + 16px);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  position: relative;
  cursor: pointer;
}
.board-scroll-thumb {
  position: absolute; top: 1px; bottom: 1px; left: 0;
  min-width: 48px;
  background: var(--gold-soft);
  border-radius: 6px;
  cursor: grab;
}
.board-scroll-thumb:hover { background: var(--gold); }
.board-scroll-thumb.dragging { background: var(--gold); cursor: grabbing; }
/* Default width. A column carrying its own width overrides both of these
   inline (app.js sets width and flex-basis together — flex-basis here would
   otherwise win over a bare inline width). */
/* A column starts at 360px and takes a share of whatever is left over, up to
   a width that is still a news column rather than a page.

   Fixed at 360px, a wider monitor bought only more empty background: measured
   with seven columns, a 3440px window left 1,140px of it and a 5120px 32:9
   left 2,820px — over half the screen. Growing has a limit, though, because a
   headline set 900px wide is worse to scan than two columns of 450px, so the
   cap is where a column stops reading as one. Past that the leftover is split
   evenly on both sides (see justify-content on .board) rather than piled up on
   the right.

   Columns the reader has sized by dragging opt out of this — applyColWidth
   sets flex-grow to 0 — because a width somebody chose is not leftover space
   to redistribute. */
.board > .feed-col {
  flex: 1 0 var(--col-base);
  width: var(--col-base);
  max-width: var(--col-grow-max);
}
/* Skipping whole off-screen columns was measured and rejected: the rows inside
   one are already off-screen, so the row rule above has taken the work
   regardless, and containing the column made the worst repaint worse. */

/* The drag handle on a column's right edge. It sits inside the column rather
   than between columns so the target is the edge you can actually see. Right
   edge only: with a handle on each edge, the line between two columns carried
   two of them doing opposite things, and grabbing the wrong one moved the
   neighbour. */
.col-grip {
  position: absolute; top: 0; bottom: 0; right: 0; width: 8px;
  cursor: col-resize;
  background: transparent;
  /* Above the article list, below anything the column pops out. */
  z-index: 2;
}
.col-grip::after {          /* the visible line, only once you're on the edge */
  content: ""; position: absolute; top: 8px; bottom: 8px; left: 3px;
  width: 2px; border-radius: 2px;
  background: transparent;
  transition: background 90ms linear;
}
.col-grip:hover::after, .col-grip:focus-visible::after, .col-grip.dragging::after {
  background: var(--gold);
}
/* While dragging, the cursor stays a resize cursor everywhere and nothing
   selects — the pointer routinely leaves the 8px handle mid-drag. */
body.col-resizing { cursor: col-resize; user-select: none; }
/* Wide enough for the decorative columns, which are drawn over the board.
   The right-hand pillar sits inboard of the action rail, so the board has to
   reserve both — a flat 70px left the last feed column 40px underneath the
   pillar with no scroll left to pull it clear. Because this is padding on a
   scroll container it extends the scrollable width too, so the last column can
   always be brought fully into the open. */
@media (min-width: 1281px) {
  .board, .empty-state {
    padding-left: calc(var(--pillar-w) + 12px);
    padding-right: calc(var(--rail-w) + var(--pillar-w) + 12px);
  }
  /* The bar runs under the board, so the pillars cover its ends too. */
  .board-scroll {
    margin-left: calc(var(--pillar-w) + 12px);
    margin-right: calc(var(--rail-w) + var(--pillar-w) + 12px);
  }
}
.feed-col {
  position: relative;   /* the resize grips anchor to the column's edges */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 5px;
  display: flex; flex-direction: column;
  /* Bounded by the board, not by a guess about the header's height. */
  max-height: 100%;
  overflow: hidden;
}

.feed-head {
  display: flex; flex-direction: column; gap: 6px;
  padding: 10px 12px;
  border-bottom: 3px double var(--gold-soft);
}
.feed-head-row { display: flex; align-items: center; gap: 8px; }
.feed-head h3 { margin: 0; font-size: 14.5px; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-badges { display: flex; gap: 4px; flex-wrap: wrap; }
.feed-badges .tag { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.feed-tools { display: flex; gap: 2px; }
.feed-tools button { opacity: 0.55; }
.feed-tools button:hover { opacity: 1; }
.feed-body { overflow-y: auto; padding: 4px 0; }
.feed-empty { color: var(--ink-muted); padding: 18px 14px; font-size: 13px; }

.tag {
  display: inline-block; font-size: 10.5px;
  background: var(--surface-2); color: var(--ink-2);
  border: 1px solid var(--border);
  padding: 0 7px; border-radius: 999px;
}

/* ---------- article rows ---------- */
.article {
  display: block;
  padding: 9px 12px;
  border-bottom: 1px solid var(--hairline);
}
/* content-visibility:auto was measured here and removed. It was worth 3.8x on
   render cost, but Chrome leaves skipped rows out of the accessibility tree
   until something scrolls to them — verified directly against the browser's AX
   tree, present with the rule off and absent with it on. A screen reader
   enumerating a column would have found eight rows out of forty. Rendering is
   chunked in app.js instead, which shortens the blocking task without ever
   making a row unreachable. */
.article:hover { background: var(--surface-2); }
.article:last-child { border-bottom: none; }
.article .title { font-weight: 600; font-size: 13.5px; }
.article .meta {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 4px; font-size: 11.5px; color: var(--ink-muted);
}
.article .summary {
  margin-top: 3px; font-size: 12px; color: var(--ink-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.article.has-thumb { display: flex; gap: 10px; align-items: flex-start; }
.article .article-text { flex: 1; min-width: 0; }
.article .thumb {
  width: 66px; height: 66px; flex: 0 0 66px;
  object-fit: cover; border-radius: 8px;
  border: 1px solid var(--border);
}

/* Importance badge: status color + icon + label (never color alone) */
.imp {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 10.5px; font-weight: 700;
  padding: 0 7px; border-radius: 999px;
  border: 1px solid currentColor;
}
.imp-critical { color: var(--critical); }
.imp-high     { color: var(--serious); }
.imp-notable  { color: var(--warning); }
.imp-routine  { color: var(--ink-muted); }

/* ---------- event clusters ---------- */
.event-group { border-bottom: 1px solid var(--hairline); }
.event-group .article { border-bottom: none; }
.event-timeline { margin: 0 12px 8px; }
.event-timeline > summary {
  cursor: pointer; font-size: 11.5px; color: var(--accent);
  padding: 2px 0; list-style-position: inside;
}
.event-timeline .article {
  border-left: 2px solid var(--hairline);
  margin-left: 6px; padding: 7px 10px;
}
.event-timeline .article .title { font-weight: 500; font-size: 12.5px; }

/* ---------- empty state ---------- */
.empty-state { text-align: center; padding: 80px 20px; color: var(--ink-2); }
.empty-state h2 { color: var(--ink); }
.empty-state .btn { margin: 6px; }
/* Startup failed, so nothing else is on screen to sit inside — this covers the
   window rather than adding to a layout that never finished being built. */
.boot-error {
  position: fixed; inset: 0; z-index: 1000;
  background: var(--page);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 40px 24px;
}
.boot-error p { max-width: 46ch; margin: 0; }
.boot-error .set-note { margin-top: 4px; }

/* ---------- side panels ---------- */
.side-panel {
  position: fixed; top: 0; right: var(--rail-w); bottom: 0; z-index: 800;
  width: 400px; max-width: 95vw;
  background: var(--surface);
  border-left: 1px solid var(--hairline);
  box-shadow: -12px 0 30px rgba(0,0,0,0.35);
  overflow-y: auto;
  padding: 0 0 20px;
}
.side-panel.wide { width: 520px; }
.side-panel-head {
  position: sticky; top: 0;
  display: flex; align-items: center; justify-content: space-between;
  background: var(--surface);
  padding: 12px 16px;
  border-bottom: 3px double var(--gold-soft);
}
.side-panel-head h2 { margin: 0; font-size: 16px; }

/* ---------- Atlas: the map board ----------
   Takes the board's place, so it fills the same space the columns do: the map
   gets everything left over and the pane keeps a fixed, readable width. */
.map-view {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  gap: 0;
  /* Same clearance as the board keeps for the collapsed action rail, and the
     11px the header's meander frieze hangs below itself. */
  padding-right: var(--rail-w);
  padding-top: 12px;
}
.world-map {
  flex: 1 1 auto;
  min-width: 0;
  border-left: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
}
/* The feed leads, on the left, because it is the thing being read. */
.map-side {
  width: 400px;
  max-width: 42vw;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  overflow: hidden;
}
/* The switches sit at the far edge, out of the eye's way. Narrower than the
   feed: it is a short list of choices, not something to read. */
.map-layers {
  width: 210px;
  flex: 0 0 auto;
  padding: 4px 14px 14px;
  background: var(--surface);
  overflow-y: auto;
}
.map-layers .set-h { padding: 0; margin: 0; }

/* Two folders, because eight hazard kinds and five base maps flat would be a
   thirteen-row wall beside the map they describe — worst on a phone, where
   this panel stacks underneath. <details> gives the disclosure behaviour,
   including keyboard and screen reader, without a line of JavaScript. */
.layer-folder { border-top: 1px solid var(--border); padding: 10px 0; }
.layer-folder:first-of-type { border-top: 0; padding-top: 4px; }
.layer-folder > summary {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer; user-select: none;
  padding: 3px 6px; margin: 0 -6px; border-radius: 6px;
  list-style: none;                 /* the default marker sits badly here */
}
.layer-folder > summary:hover { background: var(--surface-2); }
.layer-folder > summary::-webkit-details-marker { display: none; }
/* A disclosure triangle of our own, so it points the right way in both themes
   and stays put whatever else is on the row. */
.layer-folder > summary::before {
  content: "▸"; color: var(--ink-2); font-size: 11px;
  transition: transform 0.12s ease;
}
.layer-folder[open] > summary::before { transform: rotate(90deg); }
.layer-folder > summary:focus-visible {
  outline: 2px solid var(--gold-soft); outline-offset: 1px;
}
/* The all/some/none switch lives in the summary, so pressing it must not also
   open or close the folder — the click handler stops that, and this keeps the
   target from feeling like part of the heading. */
.layer-parent { display: inline-flex; align-items: center; }
.layer-parent input { margin: 0; cursor: pointer; }
.layer-group {
  display: flex; flex-direction: column; gap: 2px;
  margin-top: 6px; padding-left: 16px;   /* children sit under their parent */
}
.layer-row {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; margin: 0 -6px;
  border-radius: 6px; cursor: pointer;
  font-size: 13px; line-height: 1.3;
}
.layer-row:hover { background: var(--surface-2); }
.layer-row input { margin: 0; flex: 0 0 auto; }
.layer-row span { min-width: 0; }
body.full-board .pillar { display: none; }
/* The rail is for the boards made of columns. On Atlas the pane is the rail's
   job, and a pop-out over a map is a pop-out over the thing being read. */

/* The fire ring's switches, sitting under the radius slider in the same form. */
.loc-fire { display: flex; align-items: center; gap: 7px; font-size: 13px; }
.loc-fire input { margin: 0; }
.loc-radius em { font-style: normal; margin-left: 5px; }

/* What the air is like at a watched place: its own line under the name, so a
   long "average of 3 stations within 5 km" never fights the row's buttons for
   width. The row has to be allowed to wrap for that — and only this row, since
   .pn-row is shared with the Pantheons panel where a single line is the point. */
.loc-row { flex-wrap: wrap; }
.loc-air {
  display: flex; align-items: center; gap: 6px;
  flex: 1 0 100%; min-width: 0; margin-top: 3px;
}
/* Wraps rather than ellipsises. "…average of 3 stations withi…" loses exactly
   the half that matters: an averaged reading from next door and a single one
   from forty kilometres away are different claims, and the whole point of
   printing the basis is that the reader can tell which they have. */
.loc-air span:last-child { min-width: 0; line-height: 1.45; }
.loc-air-dot {
  flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, 0.45);
}

/* Why a Typhon layer is empty, directly under the switches it is about — which
   is the whole reason these controls left Leaflet's floating box. Was a card
   over the map; being beside the checkbox somebody just ticked is better, and
   it stops covering the thing being explained. */
.map-note {
  margin: 8px 0 0;
  padding: 7px 9px;
  background: var(--surface-2); color: var(--ink-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--gold-soft);
  border-radius: 6px;
  font-size: 12px; line-height: 1.45;
}

/* One glyph per kind of disaster: a coloured disc with the same character the
   layers panel uses, so the switch that was ticked and the marks that appeared
   are visibly the same thing. Severity keeps the colour; the glyph carries the
   kind. */
.haz-glyph { background: none; border: 0; }
.haz-glyph-dot {
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%;
  border: 1.5px solid rgba(0, 0, 0, 0.55);
  font-size: 13px; line-height: 1;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* A fire inside a watched place's ring. Same treatment as the saved-place
   star: its own artwork, so no box and no border, and a shadow to lift it off
   a busy tile without needing a second colour. */
.haz-flame {
  background: none;
  border: 0;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}

/* Typhon: what a hazard says when you press its dot. Leaflet's popup provides
   the frame; this is only the contents. */
.haz-popup strong { display: block; font-size: 14px; margin-bottom: 4px; }
.haz-popup .s-meta { font-size: 12px; line-height: 1.5; }
/* Why this one is a flame, said before any detail about the fire itself. */
.haz-popup .haz-near {
  font-size: 12px; line-height: 1.5; font-weight: 600;
  color: var(--gold); margin-bottom: 3px;
}
/* Whose figure this is, set apart from the figures themselves. */
.haz-popup .haz-attr {
  margin-top: 6px; padding-top: 5px;
  border-top: 1px solid var(--hairline);
  letter-spacing: 0.4px; text-transform: uppercase; font-size: 10px;
}

/* Each tab is a column: the same rows the board draws, in a narrower frame. */
.map-feed .article { padding: 10px 14px; border-bottom: 1px solid var(--hairline); }
.map-feed .feed-empty { padding: 14px; }
.map-pane details > summary {
  padding: 10px 14px; cursor: pointer; color: var(--ink-2);
  border-top: 1px solid var(--hairline);
}
.map-pane details[open] > summary { color: var(--ink); }

/* The head of a group: name on the left, its measure on the right, wrapping
   rather than clipping. Without the wrap "300 km" came out as "300 kr" — the
   pane is a fixed width and a long place name takes all of it. */
.map-pane .alert-head { flex-wrap: wrap; }
.map-pane .alert-head h4 { flex: 1 1 auto; min-width: 0; }
.map-pane .alert-head .s-meta { flex: 0 1 auto; white-space: nowrap; }
.map-pane .alert-block { padding: 10px 14px; }
.map-pane .alert-block:first-child { border-top: 0; }

.map-side-tabs {
  flex: 0 0 auto;
  border-bottom: 3px double var(--gold-soft);
}
.map-pane {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 10px 0;
}
/* The head of a pane: what it is showing, and the one way to add to it. */
.map-pane-head {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 4px 14px 10px;
  border-bottom: 1px solid var(--hairline);
}
.map-pane-head .set-note { flex: 1 1 auto; margin: 0; }
.map-pane-head .btn { flex: 0 0 auto; white-space: nowrap; }
.map-pane .set-h { padding: 0 14px; }
.map-pane #loc-create { padding: 10px 14px; border-bottom: 1px solid var(--hairline); }
.map-pane .pn-row { padding: 8px 14px; }

.map-pane > .set-note,
.map-pane > .loc-search,
.map-pane > .loc-form,
.map-pane > .set-h,
.map-pane > p { padding-left: 14px; padding-right: 14px; }

/* Three columns need a wide screen. At the first pinch both flanks give up
   width rather than anybody wrapping — a wrapped flex row divides the height
   between its lines, which turned the map into a two-pixel sliver. */
@media (max-width: 1180px) {
  .map-side { width: 320px; }
  .map-layers { width: 168px; padding: 4px 10px 12px; }
  .layer-row { font-size: 12px; gap: 6px; }
}
/* Narrower still and side by side leaves nothing usable, so they stack. The
   container is a column here, which is what makes `order` do the obvious
   thing: map on top, the feed under it with the room to be read, and the
   switches last — they are a handful of checkboxes and the only one of the
   three that loses nothing by being a strip at the bottom. */
@media (max-width: 820px) {
  .map-view { flex-direction: column; padding-right: 0; }
  .world-map {
    flex: 0 0 45vh; order: 1;
    border-left: 0; border-right: 0;
    border-bottom: 1px solid var(--hairline);
  }
  .map-side { width: auto; max-width: none; flex: 1 1 auto; order: 2; }
  .map-layers {
    order: 3; width: auto; flex: 0 0 auto;
    display: flex; flex-wrap: wrap; gap: 0 20px; align-items: baseline;
    border-top: 1px solid var(--hairline);
    padding: 8px 14px;
  }
  .map-layers .set-h { margin: 0; }
  /* Stacked under the map, the folders keep their disclosure but each takes a
     full line and its switches lay out sideways — the vertical list that reads
     well in a column would push the map off the screen here. */
  .layer-folder { flex: 1 0 100%; border-top: 0; padding: 4px 0; }
  .layer-folder:first-of-type { padding-top: 0; }
  .layer-group {
    flex-direction: row; flex-wrap: wrap; gap: 0 14px;
    margin-top: 4px; padding-left: 14px;
  }
  .map-note { flex: 1 0 100%; margin-top: 6px; }
}

.alert-block { padding: 10px 16px; border-bottom: 1px solid var(--hairline); }
.alert-block .alert-head { display: flex; align-items: center; gap: 8px; }
.alert-block .alert-head h4 { margin: 0; flex: 1; font-size: 14px; }
.alert-inactive { opacity: 0.55; }
.alert-events { margin-top: 6px; }

.source-tools {
  display: flex; gap: 8px; padding: 10px 16px 0;
  flex-wrap: wrap; align-items: center;
}
/* Text fields may share the row; checkboxes are sized separately below. */
.source-tools input:not([type="checkbox"]) {
  flex: 1 1 150px; min-width: 140px; padding: 6px 9px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px;
}
/* A select must never be squeezed below its own text: min-width 0 let these
   shrink until the emoji and the label printed over each other. */
.source-tools select { flex: 0 1 auto; min-width: 136px; max-width: 200px; }

/* Checkbox + label, side by side.
   Two rules used to break this: the generic `.source-tools input` /
   `.source-edit input` sizing stretched the box to a 120px padded control that
   covered its own words, and `.source-edit label`'s column direction stacked
   the box on top of them. Both are overridden here, deliberately after those
   rules so this wins. */
.check-inline {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
  font-size: 12px; color: var(--ink-2);
  white-space: nowrap;
}
/* The paywall mark on a row is a label, not a way out: the link to archive.ph
   lives in the focused view the row opens. */
.paywall-mark { margin-left: 6px; color: var(--gold); }
.archive-chip { color: var(--gold); }
.source-tools select {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px;
}
.source-edit {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 8px; padding: 12px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-2);
}
.source-edit label { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--ink-2); }
.source-edit input, .source-edit select {
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px; font-size: 12.5px;
}
.source-edit-actions { grid-column: 1 / -1; display: flex; gap: 8px; justify-content: flex-end; }
/* Must come after the generic input rules above to undo their sizing. */
.source-tools .check-inline input[type="checkbox"],
.source-edit .check-inline input[type="checkbox"] {
  flex: none;
  width: 15px; height: 15px;
  min-width: 0; padding: 0; margin: 0;
}
/* `.source-edit label` (0,1,1) outranks `.check-inline` (0,1,0), so its
   column direction kept stacking the box on top of its own words. Matching
   both selectors wins the cascade. */
.source-edit label.check-inline {
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  grid-column: 1 / -1;
}

.source-row {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 16px; border-bottom: 1px solid var(--hairline); font-size: 12.5px;
}
.source-row .s-name { flex: 1; }
.source-row .s-meta { color: var(--ink-muted); font-size: 11px; }
.s-status-ok { color: var(--good); }
.s-status-bad { color: var(--critical); }

/* ---------- modal / builder ---------- */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 900;
  background: rgba(0,0,0,0.55);
  display: flex; align-items: flex-start; justify-content: center;
  overflow-y: auto; padding: 30px 12px;
}
/* Vertically centred, for a dialog that caps its own height and scrolls
   inside. Top-aligning is right for the long reading dialogs — help, the
   release notes — where the content decides the height and the page scrolls
   behind it; centring is right for one that is always the same size. */
.modal-backdrop.centered { align-items: center; overflow: hidden; }
.modal {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  width: 760px; max-width: 100%;
}
/* A short form doesn't need the builder's width. */
.modal.narrow { width: 520px; }
/* One control width down the whole add-source form. Without this the text
   fields, the selects and the country list each took their own width and the
   right-hand edge came out ragged. */
#src-backdrop .set-row > input,
#src-backdrop .set-row > select {
  flex: 0 0 240px; width: 240px; min-width: 0;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px;
}
#src-backdrop .set-row > input[type="checkbox"] { flex: none; width: auto; }
.modal-head, .modal-foot {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px;
}
.modal-head { border-bottom: 3px double var(--gold-soft); }
.modal-head h2 { margin: 0; font-size: 16px; flex: 1; }
.modal-head .mode-switch { flex: none; }
.modal-head .mode-switch button { padding: 5px 12px; font-size: 12px; }
.modal-foot { border-top: 1px solid var(--hairline); }
.modal-foot .spacer { flex: 1; }
.modal-body { padding: 14px 18px; display: flex; flex-direction: column; gap: 14px; }

.field { display: flex; flex-direction: column; gap: 5px; }
.field > span { font-size: 12.5px; color: var(--ink-2); font-weight: 600; }
.field > span em { color: var(--ink-muted); font-weight: 400; font-style: normal; }
.field input[type="text"], .field input[type="search"], .field input[type="password"],
.field input[type="email"], .field select {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 7px 10px;
}
.field select[multiple] { min-height: 96px; }

.builder-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
.builder-grid.two { grid-template-columns: 1fr 1fr; }
.check-row { display: flex; gap: 14px; flex-wrap: wrap; font-size: 13px; color: var(--ink-2); }
.check-row label { display: flex; gap: 5px; align-items: center; }

.chip-input {
  display: flex; flex-wrap: wrap; gap: 5px;
  background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px;
  padding: 5px;
}
.chip-input input { flex: 1; min-width: 120px; background: none; border: none; color: var(--ink); outline: none; padding: 4px; }
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--surface); border: 1px solid var(--border);
  padding: 2px 8px; border-radius: 999px; font-size: 12px;
}
.chip button { background: none; border: none; color: var(--ink-muted); cursor: pointer; padding: 0; }

/* ---------- source picker (builder step 2) ----------
   A searchable checkbox list. The catalog runs to hundreds of outlets, which
   a <select multiple> could hold but nobody could work with. */
.src-pick { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.src-pick-tools { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.src-pick-tools input[type="search"] {
  flex: 1 1 220px; min-width: 160px; padding: 6px 9px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px;
}
.src-pick-chosen { display: flex; flex-wrap: wrap; gap: 5px; }
.src-pick-chosen .chip { cursor: pointer; }
.src-pick-list {
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--surface-2);
  padding: 4px;
}
.src-pick-row {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; border-radius: 5px;
  cursor: pointer;
}
.src-pick-row:hover { background: var(--surface); }
.src-pick-row input[type="checkbox"] { flex: none; margin: 0; }
/* The name takes the slack so the platform/scope/language tail stays right-
   aligned and can't be pushed off the row by a long outlet name. */
.src-pick-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.src-pick-row .s-meta { flex: none; }

#b-queries { display: flex; flex-direction: column; gap: 6px; }
.query-row .query-line { display: flex; gap: 6px; align-items: center; }
.query-row input {
  flex: 1; min-width: 0;   /* or the field refuses to shrink and the row overflows */
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 8px; padding: 7px 10px;
}

.query-status { font-size: 12px; min-height: 16px; }
.query-status.ok { color: var(--good); }
.query-status.err { color: var(--critical); }
/* Valid, and probably not what was meant — see query_advisories. Neither
   green nor red: nothing is wrong, and nothing is stopping you saving it. */
.query-status.warn { color: var(--warning); }

/* Why a column carried this story. Quiet by default; amber when the words are
   only in the body, which is the case a reader is right to be suspicious of. */
.ar-why {
  font-size: 12.5px; color: var(--ink-muted);
  border-left: 2px solid var(--hairline);
  padding-left: 9px; margin: 8px 0;
}
.ar-why-odd { color: var(--warning); border-left-color: var(--warning); }
/* The sentence the word actually sits in. Set apart from the explanation
   around it, because it is the page's words and not Delphi's. */
.ar-why-quote {
  display: inline-block; margin-top: 4px;
  color: var(--ink-muted); font-style: italic;
}

/* A saved location's star. Leaflet's default div icon is a white box with a
   border; a marker that is its own artwork wants neither. The shadow lifts it
   off a busy tile without needing a second colour. */
.loc-star {
  background: none;
  border: 0;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45));
}

/* The trial notice. Fixed to the bottom rather than the top: the top of this
   app is the board's own header and a bar there pushes every column down, which
   is a layout shift on every page load for the sake of a sentence. */
.trial-bar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 14px;
  z-index: 900;
  display: flex; align-items: center; gap: 12px;
  max-width: min(680px, calc(100vw - 32px));
  padding: 10px 12px 10px 16px;
  background: var(--surface);
  border: 1px solid var(--gold-soft);
  border-radius: 10px;
  box-shadow: 0 8px 26px rgba(0,0,0,0.4);
  font-size: 13.5px;
}
.pay-price { font-size: 20px; font-weight: 600; margin: 6px 0; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

.geo-tools { display: flex; align-items: center; gap: 10px; }
.geo-summary { font-size: 12.5px; color: var(--ink-muted); }
.builder-map { height: 340px; border-radius: 10px; border: 1px solid var(--border); }
.builder-map .leaflet-container { border-radius: 10px; }

.preview-count { font-size: 13px; color: var(--ink-2); }

/* ---------- feed builder wizard ---------- */
.wiz-nav { display: flex; gap: 4px; }
.wiz-nav button {
  background: none; border: none; cursor: pointer;
  color: var(--ink-muted); font-size: 12.5px; padding: 5px 9px;
  border-radius: 999px;
}
.wiz-nav button.active { background: var(--surface-2); color: var(--ink); font-weight: 700; }
.wiz-guide {
  margin: 0 0 4px; font-size: 13px; color: var(--ink-2);
  background: var(--surface-2); border-left: 3px solid var(--accent);
  padding: 8px 12px; border-radius: 0 8px 8px 0;
}
.wiz-step { display: flex; flex-direction: column; gap: 14px; min-height: 300px; }
.wiz-summary {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 10px; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 4px;
}
.wiz-summary-name { font-weight: 700; font-size: 14.5px; margin-bottom: 4px; }
.wiz-summary-row { font-size: 12.5px; color: var(--ink-2); }
.wiz-summary-row b { color: var(--ink); font-weight: 600; }

/* ---------- pantheons ---------- */
.pn-create { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
.pn-row {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 0; border-bottom: 1px solid var(--hairline);
}
.pn-row input[type="text"] { flex: 1; min-width: 0; }
.pn-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pn-card {
  border: 1px solid var(--border); border-radius: 7px;
  padding: 4px 10px; margin-bottom: 8px; background: var(--surface-2);
}
.pn-card .pn-row:last-child { border-bottom: none; }
.pn-detail { padding: 2px 0 8px; }
.pn-detail select { max-width: 200px; }

/* "Showing the last update" strip on a column whose refresh failed. Muted on
   purpose — the articles under it are still real, they're just not current. */
.feed-stale {
  font-size: 11.5px;
  color: var(--ink-muted);
  background: var(--surface-2);
  border-bottom: 1px solid var(--hairline);
  padding: 5px 9px;
  position: sticky;
  top: 0;
  z-index: 2;
}

/* ---------- favourite locations ---------- */
.loc-search { display: flex; gap: 8px; align-items: center; margin: 8px 0 6px; }
.loc-search input { flex: 1; min-width: 0; padding: 7px 9px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; }
.loc-results {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 168px; overflow-y: auto; margin-bottom: 8px;
  border: 1px solid var(--border); border-radius: 7px; padding: 4px;
  background: var(--surface-2);
}
.loc-results { max-height: 240px; }
.loc-result {
  background: none; border: none; color: var(--ink); cursor: pointer;
  text-align: left; padding: 6px 8px; border-radius: 5px; font-size: 13px;
  display: flex; flex-direction: column; gap: 1px; width: 100%;
}
/* Hover and the keyboard's position look the same, so a reader driving the
   list with arrow keys sees exactly what a mouse user sees. */
.loc-result:hover, .loc-result.active, .loc-result:focus-visible {
  background: var(--surface);
}
.loc-result-name { font-weight: 600; }
/* The full address of a looked-up place: two towns with the same name are only
   told apart by this, so it wraps rather than being cut off. */
.loc-result-where {
  font-size: 11.5px; color: var(--ink-muted); line-height: 1.35;
}
.loc-none, .loc-credit {
  font-size: 11.5px; color: var(--ink-muted); padding: 6px 8px;
}
.loc-credit { border-top: 1px solid var(--hairline); margin-top: 2px; }
.loc-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.loc-form input[type="text"] {
  flex: 1 1 180px; min-width: 150px; padding: 7px 9px;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px;
}
.loc-radius { display: flex; flex-direction: column; gap: 2px; font-size: 11.5px; color: var(--ink-2); }
.loc-radius input[type="range"] { width: 150px; }
.loc-form .spacer { flex: 1 1 auto; }

/* Marks an article that falls inside a favourite location. */
.near-pin {
  color: var(--gold); border: 1px solid var(--gold-soft);
  border-radius: 999px; padding: 0 6px; font-size: 10.5px; white-space: nowrap;
}

/* ---------- Pantheon modal ---------- */
.pn-tab { display: block; }
.pn-tab[hidden] { display: none; }
.pn-tab .set-row { padding: 8px 0; }
#pn-members { margin-top: 10px; }
#pn-members:empty::after {
  content: "No members yet.";
  color: var(--ink-muted); font-size: 12.5px;
}
#pn-pending:not(:empty) { margin-top: 10px; }
#pn-invite-row input { flex: 1; min-width: 0; }
/* Read-only fields for members without admin rights: legible, clearly inert. */
.pn-tab input:disabled, .pn-tab select:disabled {
  opacity: 0.75; cursor: not-allowed;
}

/* Why a save was rejected, shown in the wizard footer beside Save. */
.builder-error[hidden] { display: none; }
.builder-error {
  font-size: 12.5px;
  color: var(--critical);
  max-width: 46ch;
  line-height: 1.35;
}

/* ---------- in-flight button state ---------- */
/* Applied by feedback() while a button's async handler runs, so even an
   icon-only button visibly acknowledges the click. */
/* ---------- press feedback ----------
   Every control answers a click at once, before whatever it triggers has had
   time to happen. :active alone is not enough: a handler that starts work on
   the same tick can keep the browser from ever painting it, and on a touch
   screen the state is gone before the finger lifts. So a class is added on
   press and taken off a fifth of a second later, which always gets a frame. */
button:active, .chip:active, .loc-result:active,
.article-focus:active, .event-clickable:active, .src-pick-row:active,
.ev-related-row:active, .ar-cov-row:active {
  transform: translateY(1px);
}
button.pressed, .chip.pressed, .loc-result.pressed, .src-pick-row.pressed,
.ev-related-row.pressed, .ar-cov-row.pressed {
  filter: brightness(1.25);
}
.article-focus.pressed, .event-clickable.pressed { background: var(--surface-2); }
/* Buttons that carry their own colour shift instead, so brightness on a green
   primary doesn't wash it out. */
.btn-primary.pressed { filter: brightness(0.88); }
@media (prefers-reduced-motion: reduce) {
  button:active, .chip:active, .loc-result:active, .article-focus:active,
  .event-clickable:active, .src-pick-row:active, .ev-related-row:active,
  .ar-cov-row:active { transform: none; }
}

button.working {
  position: relative;
  opacity: 0.65;
  cursor: progress;
}
button.working::after {
  content: "";
  position: absolute; inset: 0; margin: auto;
  width: 11px; height: 11px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: btn-spin 0.6s linear infinite;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  button.working::after { animation: none; }
}

/* ---------- admin / operator console ---------- */
.admin-row {
  border: 1px solid var(--border); border-radius: 7px;
  padding: 8px 10px; margin-bottom: 8px; background: var(--surface-2);
}
.admin-row.disabled { opacity: 0.62; }
.admin-row-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.admin-name { font-weight: 700; font-size: 14px; }
.admin-badge {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 1px 6px; border-radius: 999px; border: 1px solid var(--border);
  color: var(--ink-muted);
}
.admin-badge.op { color: var(--gold); border-color: var(--gold-soft); }
.admin-badge.you { color: var(--good); border-color: var(--good); }
.admin-badge.warn { color: var(--critical); border-color: var(--critical); }
.admin-meta { font-size: 11.5px; color: var(--ink-muted); margin: 4px 0 8px; }
.admin-acts { display: flex; flex-wrap: wrap; gap: 6px; }
.btn.small.warn { color: var(--critical); border-color: var(--critical); background: transparent; }

.pop-menu {
  position: fixed; z-index: 1200;
  display: flex; flex-direction: column; min-width: 180px;
  background: var(--surface-2); border: 1px solid var(--gold-soft);
  border-radius: 7px; box-shadow: 0 8px 24px rgba(0,0,0,0.35);
  padding: 4px; gap: 2px;
}
.pop-menu button {
  background: none; border: none; color: var(--ink);
  text-align: left; padding: 7px 10px; cursor: pointer;
  border-radius: 5px; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pop-menu button:hover { background: var(--accent-strong); color: var(--accent-ink); }

/* The export menu carries a line of explanation under each format, because
   "xlsx" and "csv" are file extensions, not reasons to pick one. */
.pop-menu-head {
  padding: 6px 10px 4px; font-size: 11.5px; letter-spacing: 0.4px;
  color: var(--ink-2); text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.export-menu { min-width: 288px; max-width: 340px; }
.export-menu button {
  display: flex; flex-direction: column; gap: 2px;
  white-space: normal; line-height: 1.35;
}
.export-menu button em {
  font-style: normal; font-size: 11.5px; color: var(--ink-2);
}
.export-menu button:hover em { color: var(--accent-ink); }

/* ---------- event focus modal ---------- */
.ev-badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.ev-summary { margin: 0; font-size: 13.5px; color: var(--ink-2); }
.story-map, .event-map { height: 230px; border-radius: 10px; border: 1px solid var(--border); }
.ev-section h3 {
  margin: 4px 0 6px; font-size: 13px; color: var(--ink-2);
  text-transform: uppercase; letter-spacing: 0.4px;
}
.ev-dim { color: var(--ink-muted); font-weight: 400; font-size: 11.5px; }
#st-timeline .article { border-left: 2px solid var(--hairline); margin-left: 4px; }
.ev-sources { display: flex; gap: 6px; flex-wrap: wrap; }
.ev-related-row {
  display: flex; gap: 8px; align-items: baseline; width: 100%;
  background: none; border: none; border-bottom: 1px solid var(--hairline);
  color: var(--ink); text-align: left; cursor: pointer;
  padding: 8px 4px; font-size: 13px;
}
.ev-related-row:hover { background: var(--surface-2); }
.ev-related-title { flex: 1; }

/* ---------- story focus ----------
   The one dialog a headline opens, whether the story has a single report or
   forty. */
.story-modal { width: 680px; }
.story-modal .modal-body { max-height: 76vh; overflow-y: auto; }
.story-modal .modal-head h2 { font-size: 17px; line-height: 1.35; }
/* The report being read, inside the story's own timeline. */
.article-current {
  border-left: 3px solid var(--accent);
  background: var(--surface-2);
}
.ar-byline { margin: 0; font-size: 12.5px; color: var(--ink-muted); }
.ar-image {
  width: 100%; max-height: 260px; object-fit: cover;
  border-radius: 10px; border: 1px solid var(--border);
}
.ar-summary { margin: 0; font-size: 14px; line-height: 1.6; color: var(--ink); }
.ar-excerpt {
  margin: 0; font-size: 13.5px; line-height: 1.65; color: var(--ink-2);
  white-space: pre-wrap;                    /* the body keeps its paragraphs */
  border-left: 3px solid var(--gold-soft); padding-left: 12px;
}
.ar-excerpt-note { margin: 6px 0 0; font-size: 11.5px; color: var(--ink-muted); }
.ar-cov-row {
  border-bottom: 1px solid var(--hairline);
  padding: 8px 4px; cursor: pointer;
}
.ar-cov-row:last-child { border-bottom: none; }
.ar-cov-row:hover, .ar-cov-row:focus-visible { background: var(--surface-2); }
.ar-cov-title { font-size: 13px; color: var(--ink); }
.ar-cov-meta { font-size: 11.5px; color: var(--ink-muted); margin-top: 2px; }
/* Two columns: the fact and its value, with the labels quiet. */
.ar-facts {
  margin: 0; display: grid; grid-template-columns: max-content 1fr;
  gap: 4px 14px; font-size: 12.5px;
}
.ar-facts dt { color: var(--ink-muted); }
.ar-facts dd { margin: 0; color: var(--ink-2); }

.article-focus { cursor: pointer; }
/* A row is a button now, so it has to show where the keyboard is. */
.article-focus:focus-visible, .ar-cov-row:focus-visible, .ev-related-row:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
.event-clickable { cursor: pointer; }
.event-clickable:hover { background: var(--surface-2); }
/* previously viewed events recede; hover brings them back for re-reading */
.event-viewed { opacity: 0.5; }
.event-viewed:hover { opacity: 0.9; }
.event-viewed .event-open-hint { color: var(--ink-muted); }
.event-clickable .article:hover { background: transparent; }
.event-open-hint {
  padding: 0 12px 8px;
  font-size: 11.5px; color: var(--accent);
}
.chip-link { cursor: pointer; }
.chip-link:hover { border-color: var(--accent); color: var(--ink); }

/* ---------- sign-in gate ---------- */
.gate {
  position: fixed; inset: 0; z-index: 2000;
  background: var(--page);
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.gate-card {
  width: 380px; max-width: 94vw;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 7px;
  border-top: none;
  padding: 32px 30px 26px;
  display: flex; flex-direction: column; gap: 12px;
  text-align: center;
}
/* Same treatment as the topbar mark: the artwork is shown whole, with a
   transparent backdrop so the card's surface shows through in either theme. */
.gate-logo {
  height: 190px; width: auto;
  margin: 0 auto 2px;
  object-fit: contain;
  display: block;
  transition: opacity 160ms ease-in;
}
/* Shown only once the whole file has arrived.
   A non-interlaced PNG paints top-down as it downloads, and this artwork is
   372 KB, so on a weak connection it appeared as a head sliced in half with the
   rest of its box empty — reported from a phone on two bars of signal. The box
   is reserved at 190px either way, so revealing it late moves nothing.
   The class is added by JavaScript rather than sitting in the markup, so a
   browser that never runs it falls back to the old behaviour rather than to a
   logo that is permanently invisible. */
.gate-logo.pending { opacity: 0; }
.gate-title {
  margin: 4px 0 0; color: var(--gold);
  font-family: Georgia, "Times New Roman", serif;
  font-size: 30px; letter-spacing: 3px;
}
.gate-card .field { text-align: left; }
.gate-btn { width: 100%; margin-top: 6px; padding: 10px; font-size: 14px; }
.gate-switch { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-2); }
.gate-switch a { color: var(--accent); text-decoration: underline; }
#gate-signin, #gate-register { display: flex; flex-direction: column; gap: 10px; }

/* ---------- settings ----------
   A dialog in the middle of the screen, not a drawer down the side. Settings
   is a thing you go and do, so it takes the middle and everything else waits;
   a side panel is for something you consult while still reading, which this
   never was.

   The modal is a column with a fixed ceiling and the section bodies scroll
   inside it. Without that, five sections of wildly different length would make
   the dialog jump between a third of the screen and all of it every time a tab
   was pressed. */
.settings-modal {
  width: 720px;
  max-height: min(86vh, 860px);
  display: flex; flex-direction: column;
}
.settings-tabs {
  flex: 0 0 auto; flex-wrap: wrap;
  padding: 8px 18px; border-bottom: 1px solid var(--hairline);
}
.settings-modal .settings-tab {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  gap: 10px;
}
/* The release notes read as prose inside a dialog that is otherwise a form, so
   they get the body treatment the standalone What's-new popup already uses. */
.updates-inline { padding: 0; }
.settings-body { padding: 6px 16px 20px; display: flex; flex-direction: column; gap: 10px; }
.set-h {
  margin: 14px 0 2px; font-size: 12px; color: var(--ink-muted);
  text-transform: uppercase; letter-spacing: 0.5px;
}
.set-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 13px; }
.set-row > span { color: var(--ink-2); flex: 1; }
.set-row > span em { color: var(--ink-muted); font-style: normal; font-size: 11.5px; display: block; }
.set-row select, .set-row input[type="range"] {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px;
  min-width: 190px;
}
.set-note { font-size: 11.5px; color: var(--ink-muted); margin: 10px 0 0; }

/* compact mode: headlines only */
body.compact .article .summary, body.compact .article .thumb { display: none; }

/* ---------- FAQ modal ---------- */
.faq-modal { width: 700px; }
/* Each tab is its own scrolling body, so switching tabs starts at that tab's
   own position instead of inheriting the other one's scroll. */
.faq-modal .modal-body { max-height: 76vh; overflow-y: auto; }
.help-tab[hidden] { display: none; }
/* Service health in the operator console. A failing line is marked, because
   the useful reading of this block is "is anything wrong", at a glance. */
.admin-health { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }
.admin-health-line {
  margin: 0; padding: 8px 10px;
  border: 1px solid var(--border); border-left-width: 3px;
  border-radius: 5px;
  background: var(--surface-2);
  font-size: 12.5px; line-height: 1.5; color: var(--ink-2);
}
.admin-health-line.warn { border-left-color: var(--warning); color: var(--ink); }

/* The live-connection warning. Full width above the board, because it is not
   about any one column — while it shows, no alert can reach this tab. */
.stream-down {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 var(--board-pad, 70px) 8px;
  padding: 9px 14px;
  border: 1px solid var(--warning);
  border-radius: 6px;
  background: color-mix(in srgb, var(--warning) 12%, var(--surface));
  color: var(--ink);
  font-size: 13px; line-height: 1.5;
}
.stream-down .btn { margin-left: auto; }
@media (max-width: 720px) { .stream-down { margin: 0 10px 8px; } }

/* An action inside the help, rather than an explanation of one: Refresh lives
   in Troubleshooting because "nothing new is arriving" is the only reason to
   press it, and on the action rail it invited a click from readers who thought
   news would not arrive without it. */
.trouble-action {
  display: flex; align-items: center; gap: 14px;
  border: 1px solid var(--gold-soft);
  border-radius: 6px;
  padding: 12px 14px;
  background: var(--surface-2);
}
.trouble-action > div { flex: 1 1 auto; font-size: 13px; }
.trouble-action b { display: block; color: var(--ink); font-size: 13.5px; }
.trouble-action em {
  display: block; margin-top: 3px;
  color: var(--ink-2); font-style: normal; line-height: 1.5;
}
.trouble-action .btn { flex: 0 0 auto; }
@media (max-width: 560px) {
  .trouble-action { flex-direction: column; align-items: stretch; }
}

/* The tab buttons sit in the head beside the title; on a narrow screen the
   head wraps and they take their own row rather than squeezing the title out. */
.faq-modal .modal-head { flex-wrap: wrap; }
.faq-modal .modal-head .wiz-nav { flex: none; }
.faq-body details {
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0 14px;
  background: var(--surface-2);
}
.faq-body summary {
  cursor: pointer;
  padding: 11px 0;
  font-weight: 700; font-size: 13.5px;
  font-family: Georgia, "Times New Roman", serif;
  letter-spacing: 0.4px;
}
.faq-body details[open] summary { border-bottom: 1px solid var(--gold-soft); }
.faq-body p, .faq-body li {
  font-size: 13px; color: var(--ink-2); line-height: 1.55;
}
.faq-body b { color: var(--ink); }
.faq-body code {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 4px; padding: 1px 5px; font-size: 12px;
}
.faq-body ol, .faq-body ul { padding-left: 20px; margin: 8px 0 12px; }
.faq-body li { margin-bottom: 6px; }

/* What's-new popup: one block per date, separated by an entablature rule */
.update-entry { padding: 4px 2px 10px; }
.update-entry + .update-entry { border-top: 3px double var(--gold-soft); padding-top: 14px; }
.update-date {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 12px; font-weight: 700;
  letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--accent-strong);
}
.update-title {
  margin: 4px 0 2px; font-size: 14.5px;
  font-family: Georgia, "Times New Roman", serif;
}

/* ---------- toasts ---------- */
.toasts { position: fixed; z-index: 1000; display: flex; flex-direction: column; gap: 8px; }
.toasts.pos-br { bottom: 16px; right: calc(var(--rail-w) + 16px); }
.toasts.pos-bl { bottom: 16px; left: 16px; }
.toasts.pos-tr { top: 64px; right: calc(var(--rail-w) + 16px); }
.toasts.pos-tl { top: 64px; left: 16px; }
.toast {
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border);
  border-left: 4px solid var(--accent);
  border-radius: 10px; padding: 10px 14px;
  width: 340px; box-shadow: 0 8px 24px rgba(0,0,0,0.4);
  animation: slidein 0.25s ease-out;
}
.toast.alert-toast { border-left-color: var(--critical); }
.toast .t-title { font-weight: 700; font-size: 13px; }
.toast .t-body { font-size: 12.5px; color: var(--ink-2); margin-top: 2px; }
@keyframes slidein { from { transform: translateX(30px); opacity: 0; } to { transform: none; opacity: 1; } }

@media (max-width: 900px) {
  .stat-row { order: 3; width: 100%; justify-content: space-between; }
  .builder-grid, .builder-grid.two { grid-template-columns: 1fr; }
}

/* Phones: topbar wraps into rows, one feed column fills the screen (swipe
   between them), side panels and modals go full-width, and touch targets
   grow. */
@media (max-width: 640px) {
  .topbar { gap: 8px 10px; padding: 8px 12px; padding-right: calc(var(--rail-w) + 10px); }
  .brand { font-size: 15px; }
  .view-switch { flex: 1 1 100%; }
  /* The header's controls wrap onto their own row rather than shrinking the
     search box to nothing. */
  .topbar-actions { flex: 1 1 100%; }
  #global-search { flex: 1 1 auto; width: auto; min-width: 0; }
  .view-switch button { flex: 1; }
  .stat-tile { flex: 1; min-width: 0; padding: 4px 6px; }

  .board { padding: 14px 12px; gap: 12px; scroll-snap-type: x mandatory;
           padding-right: calc(var(--rail-w) + 10px); }
  /* One column per screen, whatever width it was dragged to on a desktop —
     !important because that width is an inline style, which a media query
     otherwise loses to. Resizing is pointless at this size, so the grips go. */
  .board > .feed-col {
    flex: 0 0 86vw !important; width: 86vw !important;
    max-width: 360px; scroll-snap-align: start;
  }
  .col-grip { display: none; }
  .board, .empty-state { padding-left: 12px; }
  .empty-state { padding-right: calc(var(--rail-w) + 10px); }

  .side-panel, .side-panel.wide { width: 100vw; max-width: 100vw; }
  /* On a phone the dialog is the screen: no room to be a box on a backdrop,
     and a capped height would only waste the little there is. */
  .modal-backdrop.centered { align-items: stretch; padding: 0; }
  .settings-modal { width: 100vw; max-height: 100vh; border-radius: 0; }

  .modal { width: 100%; }
  .modal-head, .modal-foot { flex-wrap: wrap; padding: 10px 14px; }
  .modal-head h2 { min-width: 0; font-size: 15px; }
  .modal-head .icon-btn { margin-left: auto; }   /* keep close reachable */
  .wiz-nav { flex-wrap: wrap; order: 3; width: 100%; }
  .modal-body { padding: 12px 14px; }
  .modal-foot .spacer { display: none; }
}

/* Comfortable tap targets — a property of the finger, not of the window, so
   this follows the pointer rather than the width. A phone held sideways is
   844px wide and still being tapped at. */
@media (pointer: coarse) {
  .btn, .icon-btn, .view-switch button, .wiz-nav button { min-height: 40px; }
  .modal-head .icon-btn { min-width: 40px; }
}

/* Phones, and any window too short to lay a dialog out at its natural height —
   a phone held sideways is about 360px tall. A dialog then takes the height it
   has: the title bar and the footer button stay put, and only the reading area
   between them scrolls. Without this the body claimed 76vh on its own account,
   so What's-new put "Got it" below the fold, reachable only by scrolling the
   backdrop past the entire release note. */
@media (max-width: 640px), (max-height: 620px) {
  .modal-backdrop { padding: 10px 12px; }
  .modal {
    display: flex; flex-direction: column;
    max-height: calc(100vh - 20px);
  }
  /* Phone browsers count their own toolbar inside 100vh; dvh is what is really
     on screen. The line above stays as the fallback for browsers without it. */
  @supports (height: 100dvh) { .modal { max-height: calc(100dvh - 20px); } }
  .modal-head, .modal-foot { flex: 0 0 auto; }
  .modal-body,
  .faq-modal .modal-body,
  .story-modal .modal-body {
    flex: 1 1 auto; min-height: 0;
    max-height: none; overflow-y: auto;
  }
  /* Same for the sign-in gate, whose card is taller than a sideways phone:
     scroll it rather than cut the top off and put the button out of reach. */
  .gate { align-items: flex-start; overflow-y: auto; padding: 16px 12px; }
}

/* ---------- small spacing/tone helpers ----------
   These were inline style= attributes in the markup. A Content-Security-Policy
   of style-src 'self' refuses those — the browser cannot tell an attribute the
   author wrote from one injected into the page — so they live here instead.
   (Styles set from script, element.style.x = …, are unaffected by CSP.) */
.gap-sm { margin-top: 6px; }
.gap-md { margin-top: 10px; }
.note-muted { color: var(--ink-muted); font-style: normal; }

/* ---------- dragging a column to move it ----------
   The header is the handle. Replaced ◀ ▶ buttons, which stepped one place per
   click and never showed where the column would land. */
.feed-head-draggable { cursor: grab; }
.feed-head-draggable:active { cursor: grabbing; }
/* Buttons inside the header keep their own cursor — they are not handles. */
.feed-head-draggable button,
.feed-head-draggable a { cursor: pointer; }

.col-dragging {
  opacity: 0.65;
  /* Lifted, so it reads as being carried over the board rather than part of it. */
  transform: scale(0.99);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  z-index: 5;
}
/* While a column is in the air, stop the pointer selecting headlines behind it
   and stop other columns claiming the cursor. */
body.board-reordering { user-select: none; cursor: grabbing; }
body.board-reordering .feed-col:not(.col-dragging) { transition: transform 120ms ease; }

/* Device list in the operator console. Two states worth telling apart at a
   glance: in use right now, and seen before. */
.dev-list { list-style: none; margin: .5rem 0; padding: 0; }
.dev-list li { padding: .35rem 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.dev-list li:last-child { border-bottom: 0; }
.dev-list li.in-use b::before { content: "● "; color: var(--good); }
/* An account at its limit — the state an operator is looking for. */
.admin-meta.warn { color: var(--warning); }

/* ---------- Athena: a Pantheon's coverage ----------
   Takes the board's place, like Atlas: a grid of themes against weeks needs
   the whole width, and at a year of reporting it needs to scroll sideways
   inside its own container rather than pushing the page about. */
.athena-view {
  flex: 1 1 auto;
  display: flex; flex-direction: column;
  min-height: 0; overflow: hidden;
}
.athena-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 16px;
  border-bottom: 3px double var(--gold-soft);
}
.athena-title { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.athena-title h2 { margin: 0; font-size: 17px; }
.athena-head-gap { flex: 1 1 auto; }
.athena-controls {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 8px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--surface);
}
.athena-ctl { display: flex; align-items: center; gap: 6px; font-size: 12.5px; }
.athena-ctl > span { color: var(--ink-2); }
.athena-ctl select {
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 5px 7px;
  font-size: 12.5px;
}
.athena-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 16px 28px; }

/* The grid itself. Sticky row headings, because a theme name is useless once
   it has scrolled off the left and the whole point is reading along a row. */
/* Hugs the grid rather than spanning the window, so a group with three weeks
   of reporting gets a small table and not a small table adrift in a large
   empty frame. It grows with the columns and scrolls once it runs out. */
.ath-scroll {
  overflow-x: auto; border: 1px solid var(--border); border-radius: 8px;
  width: max-content; max-width: 100%;
}
/* Sized to its content and no wider. It had `min-width: 100%`, which with only
   a handful of columns made the table stretch to fill — and a table stretches
   by giving the slack to its widest-content column, so the theme names took
   two thirds of the screen and pushed every figure into the right-hand third.
   Left to itself the grid grows with the reporting and scrolls when it has to,
   which is the behaviour that was wanted in the first place. */
.ath-matrix { border-collapse: separate; border-spacing: 0; width: max-content; }
.ath-matrix th, .ath-matrix td { padding: 0; }
.ath-corner, .ath-rowhead {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface);
  text-align: left; font-weight: 500; font-size: 12.5px;
  padding: 5px 10px;
  /* Fixed rather than a range, so the columns do not shuffle sideways when a
     longer theme name is added. Long names wrap. */
  width: 230px; min-width: 230px; max-width: 230px;
  overflow-wrap: anywhere;
  border-left: 3px solid transparent;
  border-bottom: 1px solid var(--hairline);
}
.ath-corner { z-index: 3; color: var(--ink-muted); font-size: 11px;
              text-transform: uppercase; letter-spacing: 0.5px; }
.ath-colhead {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface); color: var(--ink-2);
  font-size: 11px; font-weight: 500; white-space: nowrap;
  padding: 6px 8px; border-bottom: 1px solid var(--hairline);
}
.ath-domain-row th {
  position: sticky; left: 0;
  background: var(--surface-2);
  text-align: left; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.6px;
  padding: 7px 10px;
}
.ath-cell {
  text-align: center; border-bottom: 1px solid var(--hairline);
  min-width: 62px;
}
.ath-cell.ath-total { font-size: 12px; color: var(--ink-2); font-weight: 600; }
/* A count you can press. Sized to the cell so the shading reads as a block of
   colour along the row rather than a scatter of dots. */
.ath-hit {
  display: block; width: 100%; border: 0; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600; color: var(--ink);
  padding: 6px 4px;
}
.ath-hit:hover { outline: 2px solid var(--gold-soft); outline-offset: -2px; }

/* Lists: sources, documents, themes. */
.ath-card {
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); margin: 0 0 10px; padding: 8px 12px;
}
.ath-card > summary { cursor: pointer; display: flex; align-items: baseline; gap: 8px;
                      flex-wrap: wrap; }
.ath-card-foot { display: flex; justify-content: flex-end; padding-top: 8px; }
.ath-kind { font-size: 12px; color: var(--ink-muted); }
.ath-source { padding: 3px 0 3px 14px; font-size: 13px; }
.ath-entry { padding: 8px 0; border-top: 1px solid var(--hairline); }
.ath-entry-title { font-weight: 600; font-size: 13.5px; }
.ath-entry-body { margin: 4px 0; font-size: 13px; color: var(--ink-2); }
.ath-link { display: block; font-size: 12.5px; padding: 2px 0 2px 14px; }
.ath-chips { display: flex; flex-wrap: wrap; gap: 5px; margin: 5px 0; }
.ath-chip {
  border: 1px solid var(--border); border-radius: 999px;
  padding: 2px 9px; font-size: 11.5px; color: var(--ink-2);
}
.ath-chip-btn { cursor: pointer; background: none; }
.ath-chip-btn.on { background: var(--surface-2); color: var(--ink); font-weight: 600; }
.ath-dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; }
.ath-theme-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 2px; border-bottom: 1px solid var(--hairline); font-size: 13px;
}
.ath-theme-row .s-meta { flex: 1; }
.ath-theme-form {
  display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 8px; padding: 10px 12px; margin: 0 0 14px;
  /* Capped rather than stretched. On a wide monitor three fields sharing the
     whole width end up a hand's span apart, which reads as three unrelated
     controls instead of one short form. */
  max-width: 940px;
}
.ath-theme-form .field { flex: 1 1 180px; min-width: 0; }
/* Lists and prose in this board are read left to right, so they get a measure
   rather than the full width of a very wide screen. The grid is exempt — it is
   the one thing here that genuinely wants every pixel. */
.athena-body > div > .set-note,
.athena-body > div > .ath-card,
.athena-body > div > .ath-theme-row { max-width: 1100px; }

/* The review step. One card per item, and a dropped one stays visible rather
   than vanishing — you should be able to see what you chose not to file. */
.ath-review-head { display: flex; gap: 12px; flex-wrap: wrap; }
.ath-review-head .field { flex: 1 1 180px; }
.ath-review-item {
  border: 1px solid var(--border); border-radius: 8px;
  padding: 9px 12px; margin: 0 0 9px;
}
.ath-review-top { display: flex; align-items: center; gap: 9px; }
.ath-review-title {
  flex: 1; background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--border); border-radius: 7px; padding: 5px 8px;
  font: inherit; font-weight: 600; font-size: 13px;
}
.ath-dropped { opacity: 0.45; }
.athena-steps { margin: 6px 0; padding-left: 20px; font-size: 13.5px; }
.athena-steps li { margin: 0 0 7px; }

@media (max-width: 820px) {
  .athena-head, .athena-controls { padding: 8px 12px; }
  .athena-body { padding: 12px 12px 24px; }
  .ath-corner, .ath-rowhead {
    width: 150px; min-width: 150px; max-width: 150px; font-size: 12px;
  }
}

/* A published reliability judgement about an outlet. Deliberately quieter than
   the importance chip beside it: importance is Delphi's own reading of the
   story, this is somebody else's reading of the publisher, and the two must
   not look like they carry the same authority. */
.src-rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid currentColor;
  font-size: 0.78em;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  text-decoration: none;
  background: transparent;
}
a.src-rating:hover, a.src-rating:focus-visible { text-decoration: underline; }
.src-rating.rating-good  { color: #2f7d55; }
.src-rating.rating-mixed { color: #898781; }
.src-rating.rating-weak  { color: #b3771a; }
.src-rating.rating-bad   { color: #d03b3b; }
@media (prefers-color-scheme: dark) {
  .src-rating.rating-good  { color: #6fbf8f; }
  .src-rating.rating-mixed { color: #a8a6a0; }
  .src-rating.rating-weak  { color: #e0a447; }
  .src-rating.rating-bad   { color: #ef7f7f; }
}

/* A second entry on the same day carries no date heading of its own, so it
   needs the gap the heading used to provide. */
.update-entry.update-same-day { margin-top: -0.35rem; padding-top: 0.6rem; }

/* "No term of yours is in this article" — the answer that explains a result
   rather than justifying it, so it gets the warning tone the others do not. */
.ar-why-wide { border-left: 3px solid var(--warn, #b3771a); padding-left: 0.7rem; }

/* A count inside a sentence, sized to the two digits it can hold rather than
   stretching to the width of the field it sits in. */
.tiny-number { width: 4.2rem; }
