/* PNG Monitor.
 *
 * Two rules shape everything here:
 *
 * 1. Severity is the loudest thing on the page. The 1-5 ramp carries real
 *    meaning, so nothing decorative is allowed to compete with it. That is why
 *    Papua New Guinea's flag colours — red, black, gold — live in the masthead
 *    and the logo rather than being sprayed across the interface. Flag red sits
 *    almost exactly on top of the red that means "Critical", and a dashboard
 *    where the brand shouts as loudly as an emergency is a worse dashboard.
 *
 * 2. It is read on a phone, early, by someone deciding what to do about their
 *    day. Legible beats clever.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  /* The masthead. Near-black with a gold rule, which gives the header
     definition without borrowing any national emblem — see the note on the
     wordmark below. */
  --masthead:      #0d0f12;
  --masthead-rule: #f2c230;

  /* Page. Warm off-white rather than clinical grey. */
  --bg:          #f7f6f3;
  --bg-sunken:   #efeeea;
  --card:        #ffffff;
  --line:        #e4e2dc;
  --line-strong: #d2cfc7;

  --ink:      #16181c;
  --ink-soft: #4a4d54;
  --ink-mute: #6a6e76;   /* 4.7:1 on the page background — meta lines are small */

  /* Interactive. Deliberately not red — see note above. */
  --accent:      #1a5f7a;
  --accent-soft: #e8f1f5;
  --accent-ink:  #0f4257;
  /* Text that sits on top of --accent. In dark mode the accent becomes a light
     colour, so white on it drops to about 2.3:1 — unreadable. This token flips
     with the theme so a button is legible either way. */
  --on-accent:   #ffffff;

  /* Severity, 1 low to 5 critical.
   *
   * These are not arbitrary. Severity is an ORDERED scale, so lightness carries
   * the order: the rail colours step evenly from L 0.76 down to L 0.44, which
   * means the ramp survives colourblindness, greyscale printing and a bad
   * screen — lightness is the one perceptual channel that always works.
   *
   * They were measured, not chosen by eye. The previous ramp had Concerning and
   * Serious only ΔE 8.2 apart in normal vision and 3.2 under deutan — two
   * levels that sit side by side in a list, and most people could not tell them
   * apart. They are now ΔE 16.4 and 7.4.
   *
   * Severity never travels by colour alone anywhere in this interface: the badge
   * carries the number and the word, and the card rail also encodes it by width.
   * That secondary encoding is what makes the remaining close pair (Serious vs
   * Critical, two warm hues that cannot be pulled further apart while staying in
   * gamut) safe to ship.
   */
  --sev-1: #acb2b9;  --sev-2: #719ec4;  --sev-3: #90811d;
  --sev-4: #af3f17;  --sev-5: #991614;
  --sev-1-bg: #f2f4f6; --sev-2-bg: #e8f5ff; --sev-3-bg: #f8f4dc;
  --sev-4-bg: #ffece3; --sev-5-bg: #ffebe7;
  --sev-1-ink: #646a6f; --sev-2-ink: #446d8f; --sev-3-ink: #786900;
  --sev-4-ink: #ac421f; --sev-5-ink: #b13a31;

  /* The story-movement lines. A categorical set, not the severity ramp: these
     are three different questions, not three degrees of one, and borrowing the
     ramp would say they were. Validated as a set against the card surface —
     the worst pair is ΔE 9.5 apart under deutan and 21.4 with normal vision —
     but shape and dash carry each line as well, so no reading of the chart
     rests on telling the hues apart. */
  --series-new:        #2b6f9e;
  --series-escalating: #ae5a0c;
  --series-resolved:   #4ea45c;

  --ok:   #2f7d4f;
  --warn: #b4790f;
  --bad:  #c0261c;

  /* "A quiet 24 hours" — the one box on the front page that is good news. */
  --calm-bg:   #eef6ee;
  --calm-ink:  #2f5d34;
  --calm-edge: #cfe3cf;

  --radius:    10px;
  --radius-sm: 6px;
  --shadow:    0 1px 2px rgba(16, 18, 21, .05), 0 1px 3px rgba(16, 18, 21, .04);
  --shadow-up: 0 2px 6px rgba(16, 18, 21, .07), 0 4px 14px rgba(16, 18, 21, .05);

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:          #121417;
    --bg-sunken:   #0d0f11;
    --card:        #1a1d21;
    --line:        #292d33;
    --line-strong: #3a3f47;

    --ink:      #e9e9e5;
    --ink-soft: #b4b7bd;
    --ink-mute: #878b93;

    --accent:      #6fb3cf;
    --accent-soft: #17323d;
    --accent-ink:  #9ad0e6;
    --on-accent:   #0d1116;

    /* On a dark surface the order runs the other way — lighter means more
       prominent — so the ramp climbs from L 0.52 to L 0.80. Same measurement:
       Concerning and Serious were ΔE 4.4 apart under deutan, now 14.3. */
    --sev-1: #757b82;  --sev-2: #5f92bd;  --sev-3: #dcbe2e;
    --sev-4: #e2690e;  --sev-5: #d63a32;
    --sev-1-bg: #282a2d; --sev-2-bg: #182c3d; --sev-3-bg: #332903;
    --sev-4-bg: #412014; --sev-5-bg: #411e1b;
    --sev-1-ink: #999fa5; --sev-2-ink: #78a4c9; --sev-3-ink: #dcbe2e;
    --sev-4-ink: #e67d58; --sev-5-ink: #ec766d;

    /* Re-stepped for the dark surface rather than flipped: the same three
       hues, lightened into the band the dark card wants and validated again
       against it. */
    --series-new:        #4593c6;
    --series-escalating: #b86512;
    --series-resolved:   #45ab63;

    --ok:   #5fb37f;
    --warn: #d7a63f;
    --bad:  #e2645a;

    --calm-bg:   #15281a;
    --calm-ink:  #9bd4aa;
    --calm-edge: #29492f;

    --shadow:    0 1px 2px rgba(0, 0, 0, .4);
    --shadow-up: 0 2px 8px rgba(0, 0, 0, .5);
  }
}

/* ------------------------------------------------------------------ base -- */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent); }

/* Visible keyboard focus everywhere. The dashboard gets used one-handed on a
   phone and two-handed at a desk; neither should have to guess where it is. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

.icon {
  width: 1em; height: 1em;
  flex: none;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.14em;
}

/* --------------------------------------------------------------- masthead -- */
/* The one place the national colours appear at full strength. */

header {
  background: var(--masthead);
  border-bottom: 3px solid var(--masthead-rule);
  position: sticky; top: 0; z-index: 20;
}

.masthead {
  max-width: 1060px; margin: 0 auto;
  padding: .7rem 1rem .55rem;
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
}

.wordmark {
  display: flex; align-items: center; gap: .6rem;
  text-decoration: none; color: #fff;
}
.wordmark:hover { color: #fff; }

/* No emblem. An earlier version carried a mark derived from the national flag,
   which was dropped: this is a private monitoring tool, not anything official,
   and flag-like branding on a dashboard partners may see reads as an
   affiliation we do not have. The wordmark stands on its own. */
.wordmark b {
  font-size: 1.06rem; font-weight: 650; letter-spacing: -.015em; line-height: 1.15;
}
.wordmark span {
  display: block;
  font-size: .68rem; font-weight: 400; letter-spacing: .07em;
  text-transform: uppercase; color: rgba(255, 255, 255, .5);
}

/* Seven sections and one door to the system area. Seven is the number the
   masthead can hold at a phone width as icons, and at a laptop width as words;
   everything about the machinery rather than about the country lives behind the
   System link, which is why that link is set apart rather than being an eighth
   tab. See app/web/navigation.py. */

header nav { display: flex; gap: .1rem; margin-left: auto; flex-wrap: wrap; }
header nav a, .system-link {
  display: flex; align-items: center; gap: .38rem;
  padding: .4rem .6rem;
  color: rgba(255, 255, 255, .62);
  text-decoration: none;
  font-size: .86rem; font-weight: 500;
  border-radius: var(--radius-sm);
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}
header nav a:hover, .system-link:hover { color: #fff; background: rgba(255, 255, 255, .07); }
header nav a.on, .system-link.on {
  color: #fff; border-bottom-color: var(--masthead-rule);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
header nav a .icon, .system-link .icon { width: 1.05em; height: 1.05em; opacity: .85; }

/* The rule reads as "and, separately". */
.system-link {
  margin-left: .55rem; padding-left: .8rem;
  border-left: 1px solid rgba(255, 255, 255, .16);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  color: rgba(255, 255, 255, .5);
}

/* Below a laptop the sections take a line of their own, full width, and scroll
   sideways if the words do not fit — the wordmark and the way out to the system
   stay put on the line above. A masthead that reflows into three stacked rows is
   how a phone loses half its screen to navigation. */
@media (max-width: 860px) {
  .masthead { padding-bottom: 0; row-gap: .3rem; }
  header nav {
    order: 3; width: 100%; margin-left: 0;
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  header nav::-webkit-scrollbar { display: none; }
  .system-link { margin-left: auto; }
}

/* On a phone the words go and the icons carry it: seven icons and the system
   door fit one row at 320px, so nothing scrolls and nothing wraps. */
/* A short word under each icon, so a phone shows what each one is (F2), and
   every one a thumb-sized target: at least 44px tall. */
header nav a .short, .system-link .short { display: none; }
header nav a, .system-link { min-height: 44px; }
@media (max-width: 560px) {
  header nav { justify-content: space-between; gap: 0; }
  header nav a, .system-link {
    flex-direction: column; justify-content: center; gap: .12rem;
    min-width: 44px; padding: .3rem .2rem;
  }
  header nav a .label, .system-link .label { display: none; }
  header nav a .short, .system-link .short {
    display: block; font-size: .62rem; font-weight: 500; letter-spacing: .01em; line-height: 1;
  }
  header nav a .icon, .system-link .icon { width: 1.2em; height: 1.2em; }
  .system-link { padding-left: .45rem; margin-left: .3rem; }
}

/* Past the masthead, for a keyboard: shown only when it has focus (F2). */
.skip-link {
  position: absolute; left: .75rem; top: -3rem; z-index: 100;
  padding: .6rem .9rem; border-radius: var(--radius-sm);
  background: var(--accent); color: #fff; font-weight: 600; text-decoration: none;
}
.skip-link:focus { top: .75rem; }
main:focus { outline: none; }

.chip.needs { color: var(--sev-4-ink, #c0392b); border-color: currentColor; }

/* The provinces under the maps: quiet land, a line at each boundary, and a
   shape to tap for the province's page. */
/* A tint of the ink, so the land reads in both themes. */
.map-land path {
  fill: var(--ink); fill-opacity: .1;
  stroke: var(--ink-mute); stroke-opacity: .55; stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.map-land a:hover path, .map-land a:focus path { fill-opacity: .2; }
.map-credit { margin: .3rem 0 0; font-size: .68rem; color: var(--ink-mute); }

/* Search (F3). */
.search-form input[type=search] { flex: 1; min-width: 0; }
.search-view { margin: .6rem 0 0; }
.search-count { margin: .2rem 0 .8rem; }
.matched-note { margin: .9rem 0 -.3rem; font-size: .76rem; color: var(--ink-mute); }
.pager { display: flex; align-items: center; justify-content: center; gap: 1.2rem;
         margin: 1.2rem 0; font-size: .88rem; }
.pager a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 .6rem; }

/* Filter controls and buttons large enough for a thumb (F2). */
.filters select, .filters input, .filters button, main button { min-height: 44px; }

main { max-width: 1060px; margin: 0 auto; padding: 1.15rem 1rem 2rem; }

/* -------------------------------------------------------------- headings -- */

h2.section {
  display: flex; align-items: center; gap: .5rem;
  margin: 1.7rem 0 .7rem;
  font-size: .82rem; font-weight: 650;
  text-transform: uppercase; letter-spacing: .075em;
  color: var(--ink-soft);
}
h2.section:first-child { margin-top: .2rem; }
h2.section .icon { width: 1.15em; height: 1.15em; color: var(--ink-mute); }
h2.section .count {
  margin-left: auto;
  font-size: .82rem; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------- filters -- */

form.filters {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: .65rem .7rem;
  margin-bottom: 1.1rem;
  display: flex; gap: .45rem; flex-wrap: wrap; align-items: center;
}
form.filters .field { display: flex; align-items: center; gap: .35rem; }
form.filters label {
  font-size: .74rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-mute);
}
select, input[type=search] {
  font: inherit; font-size: .87rem;
  padding: .34rem .5rem;
  color: var(--ink); background: var(--bg-sunken);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
select:hover, input[type=search]:hover { border-color: var(--line-strong); }
button {
  font: inherit; font-size: .87rem; font-weight: 550;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .36rem .8rem;
  border: 1px solid transparent; border-radius: var(--radius-sm);
  background: var(--accent); color: var(--on-accent); cursor: pointer;
}
button:hover { background: var(--accent-ink); color: var(--on-accent); }

/* ----------------------------------------------------------------- cards -- */

.card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: .75rem .9rem .7rem 1.05rem;
  margin-bottom: .6rem;
  overflow: hidden;
}
/* The severity rail. Thicker as it gets worse, so the page can be read at a
   glance and by someone who cannot rely on colour alone. */
.card::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: var(--line-strong);
}
.card.sev-1::before { width: 3px; background: var(--sev-1); }
.card.sev-2::before { width: 4px; background: var(--sev-2); }
.card.sev-3::before { width: 5px; background: var(--sev-3); }
.card.sev-4::before { width: 6px; background: var(--sev-4); }
.card.sev-5::before { width: 7px; background: var(--sev-5); }

@media (hover: hover) {
  .card:hover { box-shadow: var(--shadow-up); border-color: var(--line-strong); }
}

.card h3 {
  margin: 0 0 .32rem;
  font-size: .97rem; font-weight: 620; line-height: 1.38; letter-spacing: -.005em;
}
.card h3 a { color: var(--ink); text-decoration: none; }
.card h3 a:hover { color: var(--accent); }
.card p.summary { margin: .3rem 0 .5rem; font-size: .89rem; color: var(--ink-soft); }

/* The scan line, above the headline: how bad, what kind, where, and whether it
   is still moving. It sits first because on a page of forty cards those are the
   four things that decide whether the headline is worth reading at all, and a
   row of badges in the same place on every card can be read down the page
   without the eye having to find it each time. */
.card .glance {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
  margin-bottom: .4rem;
}
.chip.province.none { color: var(--ink-mute); font-style: italic; }
.chip.province.mentioned { color: var(--ink-mute); border-style: dashed; font-size: .92em; }

/* ------------------------------------------------------- the second half -- */
/* Everything that is not needed to triage the card lives behind this. It is a
   <details>, so it costs no script, it opens with the keyboard, and a browser
   that finds text inside a shut one opens it by itself. */

.card details.more { margin-top: .1rem; }

/* The footer IS the control: the source count and the last update are inside
   the summary, so the whole row is the target rather than a small word at the
   end of it. */
.card details.more > summary {
  list-style: none; cursor: pointer;
  padding: .4rem 0 .05rem; margin-top: .35rem;
  border-top: 1px solid var(--line);
  -webkit-tap-highlight-color: transparent;
}
.card details.more > summary::-webkit-details-marker { display: none; }
.card details.more > summary:hover { color: var(--ink-soft); }
.card details.more > summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--radius-sm);
}
.card .card-foot .sources { display: inline-flex; align-items: center; gap: .25rem; }
.card .card-foot time { display: inline-flex; align-items: center; gap: .25rem; }
.card .card-foot .toggle {
  margin-left: auto; display: inline-flex; align-items: center; gap: .2rem;
  color: var(--accent); font-weight: 550;
}
.card .card-foot .chev { transition: transform .12s ease; }
.card details.more[open] > summary .chev { transform: rotate(90deg); }

.card .more-body { padding: .55rem 0 .15rem; font-size: .82rem; }
/* The whole summary takes the place of the first sentence or two rather than
   being printed under it — the same paragraph, grown, not said twice. A
   browser without :has() shows both, which is untidy and not broken. */
.card:has(> details.more[open]) > p.summary { display: none; }
.card .more-body p.summary.full {
  margin: 0 0 .6rem; font-size: .87rem; color: var(--ink-soft);
}
.card .more-body h4 {
  display: flex; align-items: center; gap: .35rem;
  margin: 0 0 .35rem;
  font-size: .7rem; font-weight: 650;
  text-transform: uppercase; letter-spacing: .06em; color: var(--ink-mute);
}
.card .more-body h4 .icon { width: .95em; height: .95em; }

/* One row per outlet, newest first: who said it, how much we trust them, what
   they called it, when, and the way out to their own page. */
.card .reports { list-style: none; margin: 0 0 .7rem; padding: 0; }
.card .reports li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .45rem;
  padding: .3rem 0; border-bottom: 1px dashed var(--line);
}
.card .reports li:last-child { border-bottom: none; }
.card .reports .who { font-weight: 600; color: var(--ink); }
.card .reports .chip.cred { font-size: .68rem; padding: 0 .35rem; color: var(--ink-mute); }
.card .reports .what { flex: 1 1 100%; color: var(--ink-soft); text-decoration: none; }
.card .reports .what:hover { color: var(--accent); text-decoration: underline; }
.card .reports .when {
  display: inline-flex; align-items: center; gap: .22rem;
  color: var(--ink-mute); font-size: .78rem;
}
.card .reports .out {
  display: inline-flex; align-items: center; gap: .22rem;
  color: var(--ink-mute); font-size: .78rem; text-decoration: none;
}
.card .reports .out:hover { color: var(--accent); text-decoration: underline; }

/* The rest of what is known, as a plain list of facts. */
.card .facts {
  display: grid; grid-template-columns: 7.5rem 1fr; gap: .18rem .7rem;
  margin: 0 0 .6rem;
}
.card .facts dt {
  font-size: .7rem; font-weight: 650;
  text-transform: uppercase; letter-spacing: .05em; color: var(--ink-mute);
  padding-top: .08rem;
}
.card .facts dd { margin: 0; color: var(--ink-soft); }
.card .facts a { color: var(--accent); }

.card .full-record {
  display: inline-flex; align-items: center; gap: .2rem;
  font-size: .78rem; font-weight: 550; color: var(--accent); text-decoration: none;
}
.card .full-record:hover { text-decoration: underline; }
.card .full-record .icon { width: .95em; height: .95em; }

/* On a phone the labels go above their values — a 7.5rem column leaves too
   little room for a list of names. */
@media (max-width: 480px) {
  .card .facts { grid-template-columns: 1fr; gap: .05rem; }
  .card .facts dd { margin-bottom: .35rem; }
}

.meta {
  display: flex; gap: .4rem; flex-wrap: wrap; align-items: center;
  font-size: .775rem; color: var(--ink-mute);
}
.meta .sep { opacity: .4; }
.meta a { color: var(--ink-mute); text-decoration: none; display: inline-flex; align-items: center; gap: .22rem; }
.meta a:hover { color: var(--accent); text-decoration: underline; }
.meta time, .meta .publisher { display: inline-flex; align-items: center; gap: .25rem; }

/* ---------------------------------------------------------------- badges -- */

.sev {
  display: inline-flex; align-items: center; gap: .28rem;
  font-size: .72rem; font-weight: 650; letter-spacing: .01em;
  padding: .12rem .42rem .12rem .34rem;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}
.sev .n { font-variant-numeric: tabular-nums; }
.sev.s1 { background: var(--sev-1-bg); color: var(--sev-1-ink); }
.sev.s2 { background: var(--sev-2-bg); color: var(--sev-2-ink); }
.sev.s3 { background: var(--sev-3-bg); color: var(--sev-3-ink); }
.sev.s4 { background: var(--sev-4-bg); color: var(--sev-4-ink); }
.sev.s5 { background: var(--sev-5-bg); color: var(--sev-5-ink); }
.sev.s4, .sev.s5 { font-weight: 700; }

.chip {
  display: inline-flex; align-items: center; gap: .26rem;
  font-size: .735rem;
  padding: .1rem .45rem;
  border: 1px solid var(--line-strong); border-radius: 999px;
  color: var(--ink-soft); background: var(--card);
  white-space: nowrap;
}
.chip .icon { width: .92em; height: .92em; opacity: .75; }
.chip.category { border-color: var(--line-strong); font-weight: 550; }
.chip.province { color: var(--ink-mute); }
.chip.watch    { border-color: var(--sev-4); color: var(--sev-4-ink); background: var(--sev-4-bg); }
.chip.unverified { border-color: var(--sev-3); color: var(--sev-3-ink); background: var(--sev-3-bg); font-weight: 550; }

/* Confidence — how well the reporting supports a story. Sits beside severity
   and deliberately quieter than it: plain text and three small bars, no box,
   so "4 Serious · High confidence" reads as one phrase rather than two
   badges. Only Low takes a colour. See app/web/templates/_confidence.html. */
.conf {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .735rem; color: var(--ink-mute); white-space: nowrap;
}
.conf::before { content: "·"; margin-right: .05rem; color: var(--ink-mute); }
.conf .bars { display: inline-flex; align-items: flex-end; gap: 1.5px; height: .72em; }
.conf .bars i { display: block; width: 3px; border-radius: 1px; background: var(--line-strong); }
.conf .bars i:nth-child(1) { height: 40%; }
.conf .bars i:nth-child(2) { height: 70%; }
.conf .bars i:nth-child(3) { height: 100%; }
.conf-high   .bars i,
.conf-medium .bars i:nth-child(-n+2),
.conf-low    .bars i:nth-child(1) { background: currentColor; }
.conf-high { color: var(--ink-soft); }
.conf-low  { color: var(--warn); font-weight: 550; }

/* The story page's Sources & verification panel. */
.verification { margin: .4rem 0 1.2rem; }
.verification .level {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .78rem; font-weight: 700; letter-spacing: .06em;
}
.verification .level.conf::before { content: none; }
.verification .level .bars { height: .85em; }
.verification .level .bars i { width: 4px; }
.verification .counts { font-size: .95rem; font-weight: 600; margin: .3rem 0 .1rem; }
.verification .mix { color: var(--ink-mute); font-size: .85rem; }
.verification .because { margin: .55rem 0 .3rem; }
.verification .differ {
  margin: .5rem 0; padding: .5rem .7rem; border-left: 3px solid var(--warn);
  background: var(--bg-sunken); border-radius: var(--radius-sm); font-size: .88rem;
}
.verification .differ ul { margin: .25rem 0 0; padding-left: 1.1rem; }
.verification h4 {
  display: flex; align-items: center; gap: .35rem;
  font-size: .88rem; margin: .95rem 0 .3rem;
}
.verification h4 .icon { width: 1em; height: 1em; opacity: .7; }
.verification .caveat { color: var(--ink-mute); font-size: .78rem; margin-top: .6rem; }
.verification .reasons code { font-size: .72rem; }
.reports .chip.counted { font-size: .68rem; padding: 0 .35rem; color: var(--ink-mute); }
.reports .chip.counted.independent { color: var(--ink-soft); border-color: var(--ink-mute); }
.verification .history { margin: .4rem 0 0; padding-left: 1.1rem; font-size: .85rem; }

/* ---------------------------------------------------------------- tables -- */

.wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font-size: .855rem;
  background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}
th, td { text-align: left; padding: .48rem .65rem; border-bottom: 1px solid var(--line); }
th {
  font-size: .715rem; font-weight: 650;
  text-transform: uppercase; letter-spacing: .055em;
  color: var(--ink-mute); background: var(--bg-sunken);
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--bg-sunken); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.name { font-weight: 550; }

.ok   { color: var(--ok); }
.bad  { color: var(--bad); font-weight: 600; }
.warn { color: var(--warn); }
/* A source that has never produced an item. Muted rather than red: several
   are registered against sites that publish two or three times a year. */
.quiet { color: var(--ink-mute); }

/* Definition-style table on the item page. */
table.detail th { width: 11rem; background: transparent; text-transform: none;
                  font-size: .8rem; letter-spacing: 0; color: var(--ink-mute); }

/* ----------------------------------------------------------------- misc --- */

.empty {
  display: flex; flex-direction: column; align-items: center; gap: .5rem;
  color: var(--ink-mute); font-size: .9rem;
  padding: 2.2rem 1.2rem; text-align: center;
  background: var(--card);
  border: 1px dashed var(--line-strong); border-radius: var(--radius);
}
.empty .icon { width: 1.8rem; height: 1.8rem; opacity: .4; stroke-width: 1.4; }

.notice {
  display: flex; align-items: center; gap: .45rem;
  font-size: .83rem; color: var(--ink-mute);
  margin: 0 0 .9rem;
}
.notice .icon { width: 1em; height: 1em; }

/* The digest and the weekly report are built for a mail client and bring
   their own frame, colours and spacing with them. The page only has to give
   them room and stop a wide table from pushing the layout sideways. */
.digest-body { margin: 0 0 .6rem; }
.digest-body table { max-width: 100%; }
/* Digests built before the redesign are a bare <div> with no frame of their
   own, and would otherwise sit unframed on the page next to the new ones.
   New digests are a <table>, so the child selector tells them apart without
   the archive needing to be rebuilt. */
.digest-body > div {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1.05rem;
}

.prose { font-size: .92rem; }
.prose h2 { font-size: 1.05rem; margin: 1.2rem 0 .4rem; }
.prose h3 { font-size: .95rem; margin: 1rem 0 .3rem; }
.prose ul { padding-left: 1.15rem; }
.prose li { margin: .28rem 0; }

footer {
  display: flex; flex-direction: column; align-items: center; gap: .45rem;
  color: var(--ink-mute); font-size: .78rem;
  padding: 1.5rem 1rem 2rem; text-align: center;
}
footer > div { display: flex; align-items: center; justify-content: center; gap: .45rem; }
footer .icon { width: .95em; height: .95em; opacity: .6; }
footer .signout { display: flex; align-items: center; gap: .35rem; margin: 0; }
footer .signout button { font-size: .78rem; padding: .15rem .5rem; }

/* ----------------------------------------------------------------- login -- */
/* One card, in the middle, and nothing else on the page: the phone's
   password manager should find the two fields and nothing should compete. */

.login { display: flex; justify-content: center; padding: 2rem 0; }
.login-card {
  width: 100%; max-width: 380px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-up);
  padding: 1.3rem 1.3rem 1.2rem;
}
.login-card h2 { margin: 0 0 .3rem; font-size: 1.25rem; }
.login-card .summary { margin: 0 0 1rem; font-size: .88rem; color: var(--ink-soft); }
.login-card label {
  display: block; margin-bottom: .8rem;
  font-size: .8rem; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  color: var(--ink-mute);
}
.login-card input {
  display: block; width: 100%; margin-top: .3rem;
  padding: .6rem .65rem; font: inherit; font-size: 1rem;
  color: var(--ink); background: var(--bg-sunken);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
}
.login-card input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.login-card button { width: 100%; justify-content: center; padding: .6rem; font-size: 1rem; margin-top: .3rem; }
.login-error {
  margin: 0 0 .9rem; padding: .5rem .7rem;
  border-left: 3px solid var(--sev-4); background: var(--sev-4-bg); color: var(--sev-4-ink);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0; font-size: .88rem;
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ================================================================= today == */
/* The front page: the morning email, live. One sheet, the same width as the
   email (640px), in the same order — counts, briefing, then three tiers by
   what the reader has to do about it. It borrows the email's shape rather
   than its inline styles, so it follows the dashboard's theme, including dark
   mode, which an email cannot. Emoji rather than the icon sprite for the
   section marks and categories, on purpose: they are the marks the reader
   already knows from the digest. */

.sheet {
  max-width: 640px; margin: 0 auto;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  overflow: hidden;
}
.sheet-head {
  background: var(--masthead); border-bottom: 4px solid var(--masthead-rule);
  padding: 1rem 1.15rem .9rem;
}
.sheet-title { color: #fff; font-size: 1.2rem; font-weight: 700; line-height: 1.3; }
.sheet-sub { color: rgba(255, 255, 255, .62); font-size: .8rem; margin-top: .3rem; }

.sheet-glance {
  display: flex; flex-wrap: wrap; gap: .35rem;
  padding: .7rem 1.15rem .6rem;
  background: var(--bg-sunken); border-bottom: 1px solid var(--line);
}
.sheet-glance .chip { font-weight: 650; font-size: .73rem; background: var(--card); }
.sheet-glance .chip.calm { border-color: var(--calm-edge); background: var(--calm-bg); color: var(--calm-ink); }
.sheet-glance .chip.new  { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-ink); }

/* The severity dot: the same quiet mark the email uses, in the theme's ramp. */
.dot {
  display: inline-block; width: .6em; height: .6em; border-radius: 50%;
  background: var(--sev-1); margin-right: .3em; vertical-align: .02em;
}
.dot.s1 { background: var(--sev-1); } .dot.s2 { background: var(--sev-2); }
.dot.s3 { background: var(--sev-3); } .dot.s4 { background: var(--sev-4); }
.dot.s5 { background: var(--sev-5); }

.sheet-body { padding: .1rem 1.15rem 1rem; }
.sheet-section {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .4rem;
  margin: 1.3rem 0 .45rem; padding-bottom: .35rem;
  font-size: 1rem; font-weight: 700; color: var(--ink);
  border-bottom: 2px solid var(--line);
}
.sheet-section .aside { font-weight: 400; font-size: .76rem; color: var(--ink-mute); }

.sheet-list { margin: .5rem 0 0; padding-left: 1.25rem; }
.sheet-list li { margin: 0 0 .7rem; font-size: .9rem; line-height: 1.5; }
.sheet-list.lead li { font-size: .95rem; }
.sheet-list.brief li { font-size: .85rem; margin-bottom: .45rem; }
.sheet-list a.ink { color: var(--ink); text-decoration: none; }
.sheet-list a.ink:hover { color: var(--accent); text-decoration: underline; }
.sheet-list .lead-in {
  color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px;
}
.sheet-list .marks { display: inline-flex; align-items: center; gap: .35rem; margin-right: .2rem; }
.sheet-list .mute { color: var(--ink-mute); }
.sev-word {
  display: inline-flex; align-items: center;
  font-size: .74rem; font-weight: 600; color: var(--ink-mute); white-space: nowrap;
}
.sheet-list .chip.unverified { font-size: .7rem; }

/* Everything else: one small heading per theme, as in the email. */
.sheet-theme { margin: .9rem 0 .2rem; font-size: .8rem; font-weight: 700; color: var(--ink-soft); }
/* NEW / UPDATED beside a development overnight. */
.sheet-list .state {
  margin-left: .3rem; font-size: .68rem; font-weight: 700; letter-spacing: .05em;
  color: var(--ink-mute);
}

.sheet-meta { margin-top: .15rem; font-size: .76rem; line-height: 1.5; color: var(--ink-mute); }
.sheet-meta .sep { opacity: .5; margin: 0 .2rem; }
.sheet-meta a { color: var(--ink-mute); text-decoration: none; }
.sheet-meta a.read { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.sheet-meta a:hover { color: var(--accent); }

/* "…and 4 more at this level →": what was cut, and where the rest is. */
.sheet-more { margin: .15rem 0 0 1.25rem; font-size: .78rem; }
.sheet-more a { color: var(--ink-mute); text-decoration: none; }
.sheet-more a:hover { color: var(--accent); text-decoration: underline; }

.sheet-note { margin: .7rem 0 .2rem; font-size: .88rem; color: var(--ink-soft); }
.calm-box {
  margin: .7rem 0 .2rem; padding: .75rem .9rem;
  background: var(--calm-bg); border: 1px solid var(--calm-edge);
  border-radius: var(--radius-sm); color: var(--calm-ink); font-size: .9rem;
}

.sheet-foot {
  padding: .9rem 1.15rem 1rem;
  background: var(--bg-sunken); border-top: 1px solid var(--line);
  /* ink-soft, not ink-mute: mute on the sunken ground measured 4.4:1 in the
     light theme, under the 4.5:1 small text needs (F2). */
  font-size: .78rem; line-height: 1.6; color: var(--ink-soft);
}
.sheet-foot > div { margin-bottom: .35rem; }
.sheet-foot .key { white-space: nowrap; }
.sheet-foot a { color: var(--ink-mute); }
.sheet-foot a:hover { color: var(--accent); }

@media (max-width: 480px) {
  .sheet-head { padding: .85rem .95rem .8rem; }
  .sheet-glance, .sheet-body, .sheet-foot { padding-left: .95rem; padding-right: .95rem; }
  .sheet-list { padding-left: 1.05rem; }
  .sheet-more { margin-left: 1.05rem; }
}

/* ========================================================= emergency sms == */
/* The record of what would have woken someone, and why. Status is the loudest
   thing on each row: sent and shadow in the accent, declined and expired
   quiet, failed in the bad colour. */

.emergency-mode { border-left: 4px solid var(--line-strong); }
.emergency-mode.shadow { border-left-color: var(--sev-3); }
.emergency-mode.live   { border-left-color: var(--sev-5); }
.emergency-mode .meta { margin-top: .45rem; }
.emergency-criteria { margin: -.3rem 0 1rem; }
.emergency-criteria .note { margin: .5rem 0 0; max-width: 70ch; }
.emergency-criteria ol { padding-left: 1.2rem; }
.emergency-criteria li { margin: .3rem 0; }

.chip.status { font-weight: 650; text-transform: uppercase; letter-spacing: .04em; font-size: .68rem; }
.chip.status.sent, .chip.status.shadow {
  border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft);
}
.chip.status.held { border-color: var(--sev-3); color: var(--sev-3-ink); background: var(--sev-3-bg); }
.chip.status.suppressed { border-color: var(--line-strong); color: var(--ink-soft); }
.chip.status.declined, .chip.status.expired { color: var(--ink-mute); }
.chip.status.failed { border-color: var(--sev-5); color: var(--sev-5-ink); background: var(--sev-5-bg); }

.emergency-row h3 { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; }
.emergency-row.declined, .emergency-row.expired { opacity: .8; }
.emergency-row .reason { color: var(--ink); }
.emergency-row .gate { font-style: italic; }
.sms-text {
  margin: .35rem 0 .5rem; padding: .5rem .65rem;
  background: var(--bg-sunken); border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-size: .86rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
}

/* ============================================================== overview == */
/* The graphical dashboard. Charts are plain SVG rendered on the server, so the
   page needs no JavaScript and no charting library. */

.tiles {
  display: grid; gap: .6rem; margin-bottom: 1.1rem;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.tile {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: .7rem .85rem;
}
.tile .label {
  display: flex; align-items: center; gap: .32rem;
  font-size: .73rem; font-weight: 600; letter-spacing: .045em;
  text-transform: uppercase; color: var(--ink-mute);
  margin-bottom: .28rem;
}
.tile .label .icon { width: 1em; height: 1em; }
/* Proportional figures, not tabular: equal-width digits make a large number
   look loose. tabular-nums is for columns that have to line up. */
.tile .value { font-size: 1.55rem; font-weight: 650; line-height: 1.05; letter-spacing: -.02em; }
.tile .sub   { font-size: .76rem; color: var(--ink-mute); margin-top: .2rem; }
.tile .delta { font-size: .76rem; font-weight: 600; display: inline-flex; align-items: center; gap: .2rem; }
.tile .delta.up   { color: var(--sev-5-ink); }
.tile .delta.down { color: var(--ok); }
.tile .delta.flat { color: var(--ink-mute); }

/* The one number the page leads with. Exactly one per view. */
.tile.hero { grid-column: span 2; }
.tile.hero .value { font-size: 2.9rem; letter-spacing: -.03em; }
@media (max-width: 560px) { .tile.hero { grid-column: span 2; } }

/* ------------------------------------------------- second-tier numbers --
 *
 * The strip under the tiles: the figures that qualify the four cards above
 * rather than compete with them — what started, what is getting worse, what
 * nobody solid has carried, whether the sources are answering, who is
 * gathering.
 *
 * They are second, so they are drawn second. Five more cards would have said
 * "these matter as much as those", which is exactly the wall this page had.
 * The hierarchy is size and weight — a figure two thirds of a tile's, a label
 * a shade smaller, no shadow, one shared surface with hairlines through it
 * rather than five raised ones. Not colour: on this page colour belongs to
 * severity, and a second thing shouting in it would cost the first its
 * meaning.
 */
.substats {
  display: grid; margin: 0 0 1.1rem;
  /* Wide enough that five of these wrap 3 + 2 rather than 4 + 1 on a tablet,
     and still two abreast on a phone. */
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  background: var(--bg-sunken);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
}
/* Sits directly under the cards it belongs to, as one block of numbers. */
.tiles + .substats { margin-top: -.6rem; }
/* The hairlines are a shadow drawn just outside each cell rather than a gap
   with the container showing through: the last row of a wrapped strip is
   usually short, and a gap leaves the empty part of it a grey rectangle. The
   trailing lines are clipped by the container's own border. */
.substat {
  padding: .5rem .7rem .55rem;
  box-shadow: 1px 0 0 var(--line), 0 1px 0 var(--line);
}
.substat .k {
  font-size: .7rem; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-mute);
}
.substat .v { font-size: 1.1rem; font-weight: 640; line-height: 1.15; margin-top: .12rem; }
.substat .v .of { font-size: .82rem; color: var(--ink-mute); }
.substat .note { font-size: .73rem; color: var(--ink-mute); margin-top: .12rem; }
a.substat { color: inherit; text-decoration: none; }
a.substat:hover { background: var(--accent-soft); }
a.substat:hover .k { color: var(--accent-ink); }

.panel {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: .8rem .9rem .7rem; margin-bottom: .8rem;
}
.panel > h3 {
  display: flex; align-items: baseline; gap: .5rem;
  margin: 0 0 .1rem; font-size: .9rem; font-weight: 620;
}
.panel > h3 .icon { width: 1em; height: 1em; color: var(--ink-mute); align-self: center; }
.panel .caption { margin: 0 0 .7rem; font-size: .78rem; color: var(--ink-mute); }
/* The chart keeps a readable minimum width and scrolls sideways inside its own
   container rather than shrinking. Scaled down to a phone width the whole SVG
   drops to about 45%, which takes the 10px axis labels to roughly 4px — present
   but unreadable, which is worse than asking for a swipe. The table view below
   it is the no-scrolling alternative. */
.panel .chart-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.panel svg.chart { width: 100%; min-width: 600px; height: auto; display: block; overflow: visible; }

/* ---------------------------------------------------- top developments
 *
 * The one panel on this page that is prose rather than a chart, so it is set
 * as a list of rows rather than cards in a grid: a person reads down it, and
 * cards side by side would ask them to compare instead. The whole row is the
 * link — a headline-sized target is hard to miss on a phone — and severity is
 * carried by a left edge as well as by the labelled badge, so the colour is
 * never doing the work alone.
 */

.developments .development-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column;
}
.developments .development-list > li + li { border-top: 1px solid var(--line); }

a.development {
  display: block; color: inherit; text-decoration: none;
  padding: .6rem .7rem .55rem; margin: 0 -.2rem;
  border-left: 3px solid var(--line-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
a.development.sev-1 { border-left-color: var(--sev-1); }
a.development.sev-2 { border-left-color: var(--sev-2); }
a.development.sev-3 { border-left-color: var(--sev-3); }
a.development.sev-4 { border-left-color: var(--sev-4); }
a.development.sev-5 { border-left-color: var(--sev-5); }
a.development:hover { background: var(--bg-sunken); }
a.development:hover h4 { color: var(--accent); }

a.development h4 {
  margin: 0 0 .28rem; font-size: .93rem; font-weight: 620; line-height: 1.35;
}
a.development .meta .icon { width: .92em; height: .92em; }
a.development .situation {
  margin: .3rem 0 .35rem; font-size: .85rem; line-height: 1.45; color: var(--ink-soft);
}

/* How many outlets, how fresh, and whether it is still moving — the line that
   says how much to trust the one above it. */
a.development .footing {
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem;
  font-size: .78rem; color: var(--ink-mute);
}
a.development .footing span { display: inline-flex; align-items: center; gap: .25rem; }
a.development .footing .icon { width: .95em; height: .95em; }
a.development .footing .status { font-weight: 650; color: var(--ink-soft); }
a.development .footing .status.developing { color: var(--accent-ink); }

/* Why this is on the page, in the order the points fell. Deliberately quiet
   and deliberately present: a ranking nobody can check is one nobody can
   correct. */
a.development .why {
  margin-top: .3rem; font-size: .72rem; color: var(--ink-mute); opacity: .85;
}

/* ---------------------------------------------------- what changed
 *
 * Set tighter than Top developments and without the severity badge, location
 * and source count: this is a list of headlines about movement, read in a few
 * seconds on the way to the panel below, and repeating the whole scan line
 * would make it a second copy of that panel.
 *
 * The tag carries the kind of change and is the only coloured thing on the
 * row. Three colours, not six: getting worse is warm, opening or moving is the
 * page accent, and closing is recessive — a change that says "stop watching
 * this" should not look like one that says "look at this". The word inside the
 * tag is what actually distinguishes the six, so the colour is never the only
 * channel, and severity still shows on the left edge as it does everywhere
 * else.
 */

.changes .change-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column;
}
.changes .change-list > li + li { border-top: 1px solid var(--line); }

a.change {
  display: block; color: inherit; text-decoration: none;
  padding: .5rem .7rem .45rem; margin: 0 -.2rem;
  border-left: 3px solid var(--line-strong);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
a.change.sev-1 { border-left-color: var(--sev-1); }
a.change.sev-2 { border-left-color: var(--sev-2); }
a.change.sev-3 { border-left-color: var(--sev-3); }
a.change.sev-4 { border-left-color: var(--sev-4); }
a.change.sev-5 { border-left-color: var(--sev-5); }
a.change:hover { background: var(--bg-sunken); }
a.change:hover .subject { color: var(--accent); }

a.change h4 {
  margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem;
  font-size: .9rem; font-weight: 620; line-height: 1.35;
}
a.change .subject { min-width: 0; }

/* The kind of change, in the fixed vocabulary a reader learns: NEW,
   ESCALATING, STATUS CHANGE, SPREADING, MORE REPORTING, CLOSED. */
a.change .tag {
  flex: none;
  font-size: .66rem; font-weight: 700; letter-spacing: .06em;
  padding: .08rem .38rem; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); color: var(--ink-soft); background: var(--card);
  text-transform: uppercase;
}
a.change .tag.escalating {
  border-color: var(--sev-4); color: var(--sev-4-ink); background: var(--sev-4-bg);
}
a.change .tag.new, a.change .tag.status, a.change .tag.spreading {
  border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft);
}
a.change .tag.closed { color: var(--ink-mute); }
/* Recorded changes (app/pipeline/story_changes.py) in the same three voices:
   worse or corrected, something new to know, and winding down. */
a.change .tag.escalation, a.change .tag.casualty_update, a.change .tag.correction,
a.change .tag.disputed {
  border-color: var(--sev-4); color: var(--sev-4-ink); background: var(--sev-4-bg);
}
a.change .tag.new_information, a.change .tag.official_response, a.change .tag.operational_change,
a.change .tag.location_added, a.change .tag.confirmed, a.change .tag.confidence_change,
a.change .tag.status_change {
  border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft);
}
a.change .tag.resolved, a.change .tag.deescalation { color: var(--ink-mute); }

a.change .what {
  margin: .24rem 0 .28rem; font-size: .84rem; line-height: 1.45; color: var(--ink-soft);
}

/* What was compared, so a change a person disagrees with is one they can
   check — the same habit as the reasons under a development. */
a.change .evidence {
  font-size: .72rem; color: var(--ink-mute); opacity: .85;
}
a.change .evidence .sep { margin: 0 .3rem; }

.grid-2 { display: grid; gap: .8rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 760px) { .grid-2 { grid-template-columns: 1fr; } }

/* Chart chrome — recessive by design. The data is the only loud thing. */
.chart .grid-line { stroke: var(--line); stroke-width: 1; }
.chart .grid-line.base { stroke: var(--line-strong); }
.chart .axis-text { fill: var(--ink-mute); font-size: 11px; font-family: var(--font); }
.chart .mark-context   { fill: var(--sev-1); }
.chart .mark-attention { fill: var(--sev-4); }
/* Two plots in one <svg> need naming; the panel heading can only name one. */
.chart .chart-title {
  fill: var(--ink-soft); font-family: var(--font); font-size: 10.5px;
  font-weight: 650; letter-spacing: .05em; text-transform: uppercase;
}

/* ------------------------------------------------------------- the trend
 *
 * Reporting volume and story movement, two plots in one <svg> on one x
 * geometry so a spike in the bars sits directly above whatever the stories
 * did. Every variant of both is drawn and the stylesheet shows the chosen
 * one, so switching between them costs no round trip and no script. The
 * controls are the legend: each carries the mark it switches on, and each is
 * a real radio or checkbox, so its state is told by the control rather than
 * by a colour. A browser without :has() shows every series at once — busier
 * than intended, never broken, and the table below is unaffected either way.
 */

.trend-controls {
  display: flex; flex-wrap: wrap; align-items: flex-start; gap: .5rem .9rem;
  padding: .55rem .6rem; margin-bottom: .75rem;
  background: var(--bg-sunken); border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.trend-controls fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.trend-controls legend {
  padding: 0; margin-bottom: .32rem;
  font-size: .7rem; font-weight: 600; letter-spacing: .045em;
  text-transform: uppercase; color: var(--ink-mute);
}
.trend-controls .keyrow { display: flex; flex-wrap: wrap; gap: .3rem; }
.trend-controls label.key {
  display: inline-flex; align-items: center; gap: .36rem;
  padding: .24rem .55rem .24rem .42rem;
  font-size: .82rem; color: var(--ink-mute); cursor: pointer;
  background: var(--card); border: 1px solid var(--line); border-radius: 999px;
}
.trend-controls label.key:hover { border-color: var(--line-strong); }
.trend-controls label.key input { margin: 0; accent-color: var(--accent); flex: none; }
.trend-controls label.key:has(input:checked) { border-color: var(--line-strong); color: var(--ink); }
.trend-controls label.key:has(input:checked) .name { font-weight: 600; }
.trend-controls label.key:has(input:focus-visible) {
  outline: 2px solid var(--accent); outline-offset: 1px;
}
/* An off series keeps its mark in the key, dimmed — the control says it is
   off, and the mark stays there to be recognised when it comes back on. */
.trend-controls label.key:has(input:not(:checked)) .swatch,
.trend-controls label.key:has(input:not(:checked)) .key-mark { opacity: .32; }
.trend-controls label.key .key-note { font-size: .72rem; color: var(--ink-mute); }
.trend-controls > button { align-self: flex-end; margin-left: auto; }
@media (max-width: 560px) { .trend-controls > button { margin-left: 0; } }

.trend .swatch { width: 11px; height: 11px; border-radius: 2px; flex: none; display: block; }
.trend .swatch.total           { background: var(--sev-1); }
.trend .swatch.band-attention  { background: var(--sev-4); }
.trend .swatch.band-serious    { background: var(--sev-5); }
.trend .swatch.band-unverified { background: var(--sev-3); }
.trend .key-mark { width: 26px; height: 12px; flex: none; display: block; overflow: visible; }

/* The bars. The total is the recessive grey it has always been; the slice in
   front sits on the baseline, ringed in the card colour so the two read apart
   without a border being drawn to do it. */
.trend .band { display: none; stroke: var(--card); stroke-width: 2; }
/* A page with no radios to switch between the slices — a province's own — says
   outright which one it is drawing, rather than waiting on a :checked that
   cannot happen there. */
.trend .band.shown { display: inline; }
.trend:has(#band-attention:checked)  .band-attention  { display: inline; }
.trend:has(#band-serious:checked)    .band-serious    { display: inline; }
.trend:has(#band-unverified:checked) .band-unverified { display: inline; }
.trend .band-attention  { fill: var(--sev-4); }
.trend .band-serious    { fill: var(--sev-5); }
.trend .band-unverified { fill: var(--sev-3); }
/* A full-height target per day, so the day's figures are reachable on a quiet
   day as well as a busy one. */
.trend .hit { fill: transparent; }

/* The lines, in the chart and in the key beside their toggle — one set of
   rules, so the two cannot drift apart. */
.trend .line { fill: none; stroke: var(--series, var(--ink-mute)); stroke-width: 2;
               stroke-linejoin: round; stroke-linecap: round; }
.trend .mark { fill: var(--series, var(--ink-mute)); stroke: var(--card); stroke-width: 2; }
.trend .series-new        { --series: var(--series-new); }
.trend .series-escalating { --series: var(--series-escalating); }
.trend .series-resolved   { --series: var(--series-resolved); }
.trend:has(#line-new:not(:checked))        .trend-chart .series-new        { display: none; }
.trend:has(#line-escalating:not(:checked)) .trend-chart .series-escalating { display: none; }
.trend:has(#line-resolved:not(:checked))   .trend-chart .series-resolved   { display: none; }
/* Each line named where it ends. Ink, not the series colour — the mark it sits
   against carries the identity; text that wears the colour only makes the
   colour load-bearing again. Haloed in the card colour to survive crossing a
   gridline or another line. */
.trend .series-label {
  fill: var(--ink-soft); font-family: var(--font); font-size: 10.5px; font-weight: 600;
  paint-order: stroke; stroke: var(--card); stroke-width: 3px; stroke-linejoin: round;
}
.trend .legend .quiet { color: var(--ink-mute); }

/* A legend is always present when there are two or more series. */
.legend { display: flex; gap: .9rem; flex-wrap: wrap; font-size: .76rem;
          color: var(--ink-soft); margin-top: .45rem; }
.legend span { display: inline-flex; align-items: center; gap: .32rem; }
.legend i { width: 10px; height: 10px; border-radius: 2px; display: block; }

/* Ranked bars: length carries magnitude, the written label carries identity.
   One hue — colouring eleven categories differently would burn the only free
   channel on information the bar length already shows. */
.ranked { display: flex; flex-direction: column; gap: .3rem; }
.ranked .bar-body { flex: 1; min-width: 0; }

/* The Overview's counts (F1): one quiet line of stories, not a wall of cards. */
.story-strip {
  display: flex; flex-wrap: wrap; gap: .35rem 1.1rem;
  margin: .2rem 0 1rem; font-size: .84rem; color: var(--ink-soft);
}
.story-strip > * { color: inherit; text-decoration: none; white-space: nowrap; }
.story-strip a:hover { color: var(--accent); text-decoration: underline; }
.story-strip strong { color: var(--ink); font-size: 1rem; margin-right: .15rem; }
@media (max-width: 560px) {
  .story-strip { display: grid; grid-template-columns: 1fr 1fr; gap: .4rem .8rem; }
  .story-strip > * { white-space: normal; line-height: 1.35; }
}

/* A short list in a panel: current hazards and coming up on the Overview. */
.plain-list { list-style: none; margin: .4rem 0 0; padding: 0; }
.plain-list li { padding: .45rem 0; border-bottom: 1px solid var(--line); }
.plain-list li:last-child { border-bottom: 0; }
.plain-list a.ink { color: var(--ink); font-weight: 550; font-size: .9rem; text-decoration: none; }
.plain-list a.ink:hover { color: var(--accent); text-decoration: underline; }
.plain-list .meta { margin-top: .15rem; font-size: .76rem; color: var(--ink-mute); }
.plain-list .meta .status { font-weight: 600; color: var(--ink-soft); }

/* Worth a look: the most serious items (was inline styles). */
.worth { padding: .4rem 0; border-bottom: 1px solid var(--line); }
.worth-head { display: flex; gap: .45rem; align-items: baseline; }
.worth-head a { font-size: .87rem; font-weight: 550; color: var(--ink); text-decoration: none; }
.worth-head a:hover { color: var(--accent); text-decoration: underline; }
.worth .meta { margin-top: .22rem; }
.ranked .row { display: grid; grid-template-columns: 1fr 2.6rem; gap: .5rem; align-items: center; }
.ranked .bar-label { display: flex; align-items: center; gap: .38rem; min-width: 0; }
.ranked .bar-label .icon { width: .95em; height: .95em; color: var(--ink-mute); flex: none; }
.ranked .bar-label .name {
  font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ranked .track { position: relative; height: 6px; background: var(--bg-sunken);
                 border-radius: 3px; margin-top: .18rem; overflow: hidden; }
.ranked .fill  { position: absolute; inset: 0 auto 0 0; background: var(--accent);
                 border-radius: 0 3px 3px 0; }
.ranked .fill.s1 { background: var(--sev-1); }
.ranked .fill.s2 { background: var(--sev-2); }
.ranked .fill.s3 { background: var(--sev-3); }
.ranked .fill.s4 { background: var(--sev-4); }
.ranked .fill.s5 { background: var(--sev-5); }
.ranked .count { text-align: right; font-size: .82rem; font-weight: 600;
                 font-variant-numeric: tabular-nums; color: var(--ink-soft); }

/* Every chart has a table twin, so no value is reachable only by hovering. */
details.table-view { margin-top: .6rem; }
details.table-view > summary {
  font-size: .76rem; color: var(--ink-mute); cursor: pointer;
  list-style: none; display: inline-flex; align-items: center; gap: .3rem;
}
details.table-view > summary::-webkit-details-marker { display: none; }
details.table-view > summary:hover { color: var(--accent); }
details.table-view table { margin-top: .45rem; box-shadow: none; }

.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block;
              margin-right: .35rem; vertical-align: middle; }
.status-dot.ok  { background: var(--ok); }
.status-dot.bad { background: var(--sev-5); }

/* A tile that is a way in to somewhere, used on the system page. Same box as a
   reading tile, so the two can sit in one grid. */
a.tile { display: block; color: inherit; text-decoration: none; }
a.tile:hover { border-color: var(--accent); box-shadow: var(--shadow-up); }
a.tile:hover .label { color: var(--accent-ink); }
.tile .value .of { font-size: 1rem; color: var(--ink-mute); }

/* =================================================================== map == */
/* Plotted here, not fetched: no tile server, no mapping library, no basemap.
   Everything drawn is a real coordinate — the graticule is true degrees, each
   province sits at its main town and each earthquake at its epicentre — and
   there is deliberately no coastline, because we have no boundary data to draw
   one from offline and a remembered outline is a made-up one. See app/pipeline/geo.py. */

.panel svg.chart.map { min-width: 680px; }

.map .map-town { fill: var(--line-strong); }

/* An invisible disc around a quiet province's town, so the dot is a target a
   thumb can hit. It must catch the pointer while drawing nothing. */
.map .map-reach { fill: transparent; }
.map a:hover .map-reach { fill: var(--accent-soft); }
.map a:hover .map-town { fill: var(--accent); }

.map .map-mark {
  fill-opacity: .72;
  stroke: var(--card); stroke-width: 1.5;
}
.map .map-mark.s1 { fill: var(--sev-1); }
.map .map-mark.s2 { fill: var(--sev-2); }
.map .map-mark.s3 { fill: var(--sev-3); }
.map .map-mark.s4 { fill: var(--sev-4); }
.map .map-mark.s5 { fill: var(--sev-5); }

/* Earthquakes are rings, not discs: a second kind of thing on the same map has
   to differ by shape as well as by colour. */
.map .map-quake { fill: none; stroke-width: 1.8; stroke: var(--sev-3); }
.map .map-quake.s1 { stroke: var(--sev-1); }
.map .map-quake.s2 { stroke: var(--sev-2); }
.map .map-quake.s3 { stroke: var(--sev-3); }
.map .map-quake.s4 { stroke: var(--sev-4); }
.map .map-quake.s5 { stroke: var(--sev-5); }

/* A halo, so a town name stays readable where it crosses a degree line or
   another mark. */
.map .map-label {
  font-family: var(--font); font-size: 11px; font-weight: 600;
  fill: var(--ink);
  stroke: var(--card); stroke-width: 3px; paint-order: stroke;
}
.map .map-count { font-weight: 700; fill: var(--accent-ink); }

.map a:hover .map-mark { fill-opacity: .95; stroke: var(--ink); }
.map a:hover .map-quake { stroke-width: 2.6; }

.legend i.quake-key {
  background: transparent; border: 2px solid var(--sev-3); border-radius: 50%;
}

/* The story map, /map/stories. Same frame, same province points, counting
   happenings rather than articles — see app/pipeline/geo.py. */

/* A story at a coordinate a feed actually published: a filled disc with a ring,
   so it reads as a pin dropped on the map rather than as another province
   circle. Everything else on this map is a province drawn at its town, and the
   two must not be mistaken for each other. */
.map .map-event {
  fill: var(--card); stroke: var(--sev-3); stroke-width: 2.6;
}
.map .map-event.s1 { stroke: var(--sev-1); }
.map .map-event.s2 { stroke: var(--sev-2); }
.map .map-event.s3 { stroke: var(--sev-3); }
.map .map-event.s4 { stroke: var(--sev-4); }
.map .map-event.s5 { stroke: var(--sev-5); }
.map a:hover .map-event { stroke-width: 3.4; }

/* The province being read, so the map says where the list underneath it came
   from. A ring rather than a colour change: the fill is carrying severity. */
.map .map-mark.selected { stroke: var(--ink); stroke-width: 2.6; fill-opacity: .95; }

.legend i.event-key {
  background: var(--card); border: 2px solid var(--sev-3); border-radius: 50%;
}

/* What the map cannot place, said in words under it: stories about the whole
   country, and stories whose reports named nowhere at all. They are not
   dropped and they are not guessed at — they are counted here. */
.offmap { margin-top: .9rem; border-top: 1px solid var(--line); padding-top: .7rem; }
.offmap h4 {
  font-size: .78rem; font-weight: 650; letter-spacing: .03em;
  text-transform: uppercase; color: var(--ink-mute);
  display: flex; align-items: center; gap: .35rem; margin: 0 0 .45rem;
}
.offmap ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .4rem; }
.offmap li {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  font-size: .85rem;
}
.offmap li .tally { color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.offmap li .why { color: var(--ink-mute); font-size: .78rem; }
/* ============================================================= province == */
/* One province's own page. Built out of the parts the rest of the dashboard
   already uses — the tiles, the ranked bars, the fortnight chart, the ranked
   developments, a table twin under each — so it is the familiar grammar
   pointed at one place rather than a fifth way of drawing a number. What is
   new here is the head, the ways out of it, and the province name being a
   link wherever it appears. */

.province-head { margin: 0 0 .8rem; }
.province-head h2 {
  display: flex; align-items: baseline; gap: .45rem; flex-wrap: wrap;
  margin: 0 0 .2rem; font-size: 1.22rem; font-weight: 650; letter-spacing: -.015em;
}
.province-head h2 .icon { width: .82em; height: .82em; color: var(--ink-mute); align-self: center; }
.province-head .caption { margin: 0 0 .45rem; max-width: 66ch; font-size: .82rem; color: var(--ink-mute); }

/* The ways out: the same province on the pages that show it differently. */
.province-ways { display: flex; flex-wrap: wrap; gap: .45rem; }
.province-ways a {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .78rem; font-weight: 550; text-decoration: none;
  padding: .22rem .55rem; border-radius: 999px;
  border: 1px solid var(--line-strong); background: var(--card); color: var(--ink-soft);
}
.province-ways a:hover { border-color: var(--accent); color: var(--accent-ink); }
.province-ways .icon { width: .95em; height: .95em; opacity: .75; }

/* A province chip is a link now, on every page that prints one. It has to keep
   reading as a chip rather than as body-text link: no underline, and the
   colour only moves on hover. */
a.chip.province { text-decoration: none; }
a.chip.province:hover { border-color: var(--accent); color: var(--accent-ink); }

/* A table of six columns, one of them a headline, is unreadable squeezed into a
   phone: the title column ends up one word per line. `.wrap` is already a
   horizontal scroller, so give these a readable floor and let it do its job —
   the same trade the charts make, and for the same reason. A swipe beats four
   columns of confetti. */
.wrap table.wide { min-width: 44rem; }
/* And the headline column needs a floor of its own, or the narrow columns take
   their share first and the title is still one word per line. */
.wrap table.wide th:first-child,
.wrap table.wide td:first-child { min-width: 15rem; }

/* Under a story's title in the active-stories table: what kind it is and since
   when, without a column of its own. */
td .row-sub {
  display: flex; align-items: center; gap: .3rem; flex-wrap: wrap;
  margin-top: .15rem; font-size: .75rem; font-weight: 400; color: var(--ink-mute);
}
td .row-sub .icon { width: .95em; height: .95em; }

/* A second heading inside a panel — the weather outlook under the hazard
   table. Quieter than the panel's own h3, which is still the subject. */
.panel h4.sub-head {
  display: flex; align-items: center; gap: .35rem;
  margin: .9rem 0 .45rem; font-size: .8rem; font-weight: 620; color: var(--ink-soft);
}
.panel h4.sub-head .icon { width: 1em; height: 1em; color: var(--ink-mute); }

/* The ranked bars name a province on the overview, and the name is the way in
   to it. Same rule as the chip: it must not turn the panel into a page of
   links. */
.ranked .name a { color: inherit; text-decoration: none; }
.ranked .name a:hover { color: var(--accent); }

/* =============================================================== stories == */
/* A story is a card — same shape, same expander, see _story.html — with the
   things only a story can say: that it is one happening, how many outlets have
   carried it, and whether it is still moving. That is what the monitor and the
   stories list are made of. One story's own page, /events/{id}, keeps its own
   heading block below. */

/* The story page: who else ran a report, under the report it was folded into.
   Quieter than the report itself — it is corroboration, not another story. */
.card .also {
  margin: .5rem 0 .1rem; font-size: .72rem; font-weight: 650;
  letter-spacing: .04em; text-transform: uppercase; color: var(--ink-mute);
}
.chip.count { border-style: dashed; }

.card.story .story-mark {
  border-color: var(--accent); color: var(--accent);
  background: var(--accent-soft); font-weight: 600;
}
.card.story.closed { opacity: .72; }

/* The heading of one story's own page. Not a card: it is the subject of the
   page rather than one thing on a list of them, so it is not competing with
   the reports underneath it and does not fold away. */
.event {
  background: var(--card); border: 1px solid var(--line);
  border-left: 4px solid var(--line-strong);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: .75rem .9rem .7rem; margin-bottom: .7rem;
}
.event.sev-1 { border-left-color: var(--sev-1); }
.event.sev-2 { border-left-color: var(--sev-2); }
.event.sev-3 { border-left-color: var(--sev-3); }
.event.sev-4 { border-left-color: var(--sev-4); }
.event.sev-5 { border-left-color: var(--sev-5); }
.event.closed { opacity: .72; border-left-color: var(--line-strong); }

.event h3 { margin: 0 0 .3rem; font-size: .97rem; font-weight: 620; line-height: 1.35; }
.event h3 a { color: var(--ink); text-decoration: none; }
.event h3 a:hover { color: var(--accent); }
.event p.summary { margin: .25rem 0 .5rem; font-size: .88rem; color: var(--ink-soft); }

/* "Developing" has to look different from a severity badge — it is about
   freshness, not seriousness, and the two must not be confused. */
.chip.developing {
  border-color: var(--accent); color: var(--accent);
  background: var(--accent-soft); font-weight: 600;
}
/* A place the reports named, where it is finer than the province. No icon: the
   province chip beside it already carries one, and a row of pins reads as
   noise. */
.chip.place { color: var(--ink-mute); border-style: dotted; }

.card .card-foot .since { display: inline-flex; align-items: center; gap: .25rem; }
.card .reports .chip.cred.copy { border-style: dashed; }

/* The report you are reading, on its own story. Without it the list on an
   article's page is a list you have to search for yourself in. */
.card .reports li.here { background: var(--bg-sunken); }
.card .reports li.here .who { color: var(--accent); }

/* ============================================================== whats on == */

.whatson-group { margin-bottom: 1.4rem; }
.gathering {
  background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: .65rem .85rem; margin-bottom: .5rem;
}
.gathering.elsewhere { border-left-color: var(--line-strong); }
.gathering h3 { margin: 0 0 .25rem; font-size: .93rem; font-weight: 600; line-height: 1.35; }
.gathering h3 a { color: var(--ink); text-decoration: none; }
.gathering h3 a:hover { color: var(--accent); }
.gathering p { margin: .2rem 0 .45rem; font-size: .86rem; color: var(--ink-soft); }

/* When and where get their own line — they are the reason to look at this page. */
.whenwhere {
  display: flex; gap: .8rem; flex-wrap: wrap;
  font-size: .82rem; margin-bottom: .35rem;
}
.whenwhere span { display: inline-flex; align-items: center; gap: .3rem; }
.whenwhere .icon { width: .95em; height: .95em; color: var(--ink-mute); }
.whenwhere .value { font-weight: 550; }
.whenwhere .missing { color: var(--ink-mute); font-weight: 400; font-style: italic; }

/* What's on, by when it is on. An event card leads with a calendar tile and
   the date, not a headline and a byline, so it does not read as one more
   article: date, name, place and status are what is scanned for; who
   reported it and when sits behind the card's foot. One column at any width,
   nothing sideways. */
.wo-jump { display: flex; flex-wrap: wrap; gap: .35rem; margin: -.4rem 0 .8rem; }
.wo-jump .chip { text-decoration: none; color: var(--ink-soft); padding: .2rem .55rem; font-size: .78rem; }
.wo-jump .chip .n { color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.wo-section { scroll-margin-top: 4.5rem; }
.wo-section h2.section { margin-top: 1.3rem; }

.wo-card {
  display: flex; gap: .7rem; align-items: flex-start;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  padding: .6rem .75rem; margin-bottom: .5rem;
}
.wo-tile {
  flex: 0 0 3.1rem; display: flex; flex-direction: column; align-items: center;
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--bg-sunken); padding: .2rem 0 .25rem; line-height: 1.1;
}
.wo-tile .dow, .wo-tile .mon { font-size: .66rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-mute); }
.wo-tile .day { font-size: 1.15rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.wo-tile.none { border-style: dashed; background: transparent; padding: .6rem 0; }
.wo-tile.none .icon { width: 1.2rem; height: 1.2rem; color: var(--ink-mute); }

.wo-body { flex: 1; min-width: 0; }
.wo-body h3 { margin: 0 0 .15rem; font-size: .95rem; font-weight: 650; line-height: 1.3; overflow-wrap: anywhere; }
.wo-body h3 a { color: var(--ink); text-decoration: none; }
.wo-body h3 a:hover { color: var(--accent); }
.wo-when { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; font-size: .86rem; }
.wo-when strong { font-weight: 650; color: var(--accent); }
.wo-when strong.struck { text-decoration: line-through; color: var(--ink-mute); }
.wo-note { font-size: .76rem; color: var(--ink-mute); font-style: italic; }
.wo-where {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem;
  font-size: .82rem; margin-top: .2rem;
}
.wo-where > .icon { width: .95em; height: .95em; color: var(--ink-mute); }
.wo-where .value { font-weight: 550; }
.wo-where .missing { color: var(--ink-mute); font-style: italic; }
.wo-summary {
  margin: .3rem 0 0; font-size: .83rem; color: var(--ink-soft);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.wo-status {
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  padding: .08rem .4rem; border-radius: 999px; border: 1px solid;
}
.wo-status.cancelled { border-color: var(--sev-5); color: var(--sev-5-ink); background: var(--sev-5-bg); }
.wo-status.postponed, .wo-status.relocated { border-color: var(--sev-3); color: var(--sev-3-ink); background: var(--sev-3-bg); }
.wo-status.rescheduled { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.wo-card.cancelled { opacity: .8; }
.wo-card.cancelled h3 a { color: var(--ink-soft); }

.wo-sources { margin-top: .35rem; font-size: .76rem; color: var(--ink-mute); }
.wo-sources summary {
  cursor: pointer; display: flex; flex-wrap: wrap; align-items: center; gap: .35rem;
  list-style: none;
}
.wo-sources summary::-webkit-details-marker { display: none; }
.wo-sources summary .src::after { content: " ▾"; }
.wo-sources[open] summary .src::after { content: " ▴"; }
.wo-sources ul { margin: .35rem 0 0; padding-left: 1rem; }
.wo-sources li { margin-bottom: .25rem; overflow-wrap: anywhere; }
.wo-sources li > a { color: var(--ink-soft); }
.wo-sources .meta { display: block; }
.wo-sources .meta .icon { width: .8em; height: .8em; margin-left: .15rem; }
.wo-more { font-size: .8rem; color: var(--ink-mute); margin: .2rem 0 0; }

@media (max-width: 480px) {
  .wo-card { gap: .55rem; padding: .55rem .6rem; }
  .wo-tile { flex-basis: 2.7rem; }
  .wo-tile .day { font-size: 1rem; }
}

/* ---------------------------------------------------------------- approvals
 *
 * This page is where someone decides whether a message goes to real people, so
 * it is styled to slow the reader down rather than speed them up: the editable
 * message is a full-width field they have to look at, the reason sits above it,
 * and the approve button is the only filled button on the card.
 */

.note {
  margin: -.2rem 0 1rem; max-width: 62ch;
  font-size: .88rem; color: var(--ink-soft);
}
.note code {
  padding: .05rem .3rem; border-radius: var(--radius-sm);
  background: var(--bg-sunken); font-size: .92em;
}

/* What just happened, after a redirect. Not an alert box — a line of text, in
   the flow, which is what it is. */
.said {
  margin: 0 0 1rem; padding: .5rem .7rem;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft); color: var(--ink);
  font-size: .9rem; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

nav.tabs { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: 1rem; }
nav.tabs a {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .7rem; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--card);
  color: var(--ink-soft); text-decoration: none;
  font-size: .85rem; text-transform: capitalize;
}
nav.tabs a:hover { border-color: var(--line-strong); color: var(--ink); }
nav.tabs a .icon { width: 1em; height: 1em; color: var(--ink-mute); }
nav.tabs a.on .icon { color: var(--accent-ink); }
/* The section tabs carry written labels — "At a glance", not "At A Glance". */
nav.tabs.subnav a { text-transform: none; }
nav.tabs a.on {
  background: var(--accent-soft); border-color: var(--accent);
  color: var(--accent-ink); font-weight: 600;
}
nav.tabs .count {
  padding: 0 .35rem; border-radius: 999px;
  background: var(--bg-sunken); color: var(--ink-mute);
  font-size: .8em; font-variant-numeric: tabular-nums;
}
nav.tabs a.on .count { background: var(--card); color: var(--accent-ink); }

.flag-head {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: .5rem;
}
.chip.audience {
  border-color: var(--accent); color: var(--accent-ink);
  background: var(--accent-soft); font-weight: 550;
}
.flag .why {
  margin: .4rem 0 .6rem; font-size: .88rem; color: var(--ink-soft);
}
.flag .why b { color: var(--ink); font-weight: 600; }

.flag .quiet, .flag .waiting {
  display: flex; align-items: flex-start; gap: .4rem;
  margin: .5rem 0; padding: .45rem .6rem;
  border: 1px dashed var(--line-strong); border-radius: var(--radius-sm);
  background: var(--bg-sunken); color: var(--ink-soft); font-size: .85rem;
}
.flag .quiet .icon { flex: none; width: 1.1em; height: 1.1em; margin-top: .1em; }

.flag-form { margin: .7rem 0 0; }
.flag-form label {
  display: block; margin-bottom: .6rem;
  font-size: .82rem; font-weight: 600; color: var(--ink-soft);
}
.flag-form textarea, .flag-form input[type=text] {
  display: block; width: 100%; margin-top: .25rem;
  padding: .45rem .55rem; font: inherit; font-size: .88rem;
  color: var(--ink); background: var(--bg-sunken);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  resize: vertical;
}
.flag-form textarea:focus, .flag-form input[type=text]:focus {
  outline: 2px solid var(--accent); outline-offset: 1px;
  border-color: var(--accent);
}
.flag-form small {
  display: block; margin-top: .2rem;
  font-weight: 400; font-size: .78rem; color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}
.flag-form.inline {
  display: inline-flex; gap: .4rem; align-items: center;
  margin: .3rem .4rem 0 0;
}
.flag-form.inline input[type=text] { width: 15rem; margin-top: 0; }
.flag .actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }

/* The only filled button on the card, because it is the only one that reaches
   anybody. */
button.approve { background: var(--accent); color: var(--on-accent); }
button.reject, button.quiet-button {
  background: transparent; color: var(--ink-soft);
  border: 1px solid var(--line-strong);
}
button.reject:hover {
  background: var(--sev-4-bg); color: var(--sev-4-ink); border-color: var(--sev-4);
}
button.quiet-button:hover { background: var(--bg-sunken); color: var(--ink); }

/* A message, shown as it will arrive: preserved line breaks, no prose styling. */
td.msg { white-space: pre-wrap; font-size: .88rem; }

.propose-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: .6rem;
  padding: .4rem 0; border-bottom: 1px solid var(--line);
}
.propose-row .note { margin: 0; }

/* Related stories on a story's page: separate developments, linked not merged. */
.related-stories { list-style: none; margin: 0 0 1rem; padding: 0; }
.related-stories li {
  display: flex; flex-wrap: wrap; gap: .25rem .6rem; align-items: baseline;
  padding: .45rem 0; border-bottom: 1px solid var(--line);
}
.related-stories li:last-child { border-bottom: none; }
.related-stories a { color: var(--ink); text-decoration: none; font-weight: 550; flex: 1 1 16rem; }
.related-stories a:hover { color: var(--accent); text-decoration: underline; }
.related-stories .when { font-size: .8rem; color: var(--ink-mute); }

/* ========================================================== story page == */
/* One story's own page (event.html). One column at every width: it is read
   top to bottom, and the order is the priority — situation, changes, facts,
   verification — so a wide screen gets a comfortable measure, not a second
   column competing with the first. */

.story-head h1 { margin: 0 0 .45rem; font-size: 1.28rem; font-weight: 680; line-height: 1.28; }
.story-head .line1, .story-head .line2, .story-head .line3 {
  display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .45rem;
}
.story-head .line1 { margin-bottom: .35rem; }
.story-head .line2 { font-size: .9rem; color: var(--ink-soft); }
.story-head .line2 a { color: inherit; }
.story-head .line2 .icon, .story-head .line2 span .icon { width: .95em; height: .95em; vertical-align: -.1em; margin-right: .2rem; opacity: .7; }
.story-head .line3 { font-size: .8rem; color: var(--ink-mute); margin-top: .2rem; }
.story-head .sep { color: var(--ink-mute); }

.situation { padding: .85rem 1rem; }
.situation .lead { margin: 0 0 .35rem; font-size: 1.02rem; line-height: 1.5; color: var(--ink); }
.situation .attrib, .situation .status, .situation .newer {
  margin: .25rem 0 0; font-size: .82rem; color: var(--ink-mute);
}
.situation .figures { margin: .55rem 0 0; font-weight: 600; font-size: .92rem; }
.situation .figures a { font-weight: 400; font-size: .8rem; margin-left: .3rem; }
.situation .status { color: var(--ink-soft); }
.caveat-line {
  display: flex; gap: .35rem; align-items: baseline;
  margin: .45rem 0 0; font-size: .85rem; color: var(--sev-3-ink);
}
.caveat-line .icon { width: .95em; height: .95em; flex: none; }
.muted { color: var(--ink-mute); }

.cite { font-size: .72rem; white-space: nowrap; }
.cite a { color: var(--ink-mute); text-decoration: none; margin-left: .15rem; }
.cite a:hover { color: var(--accent); text-decoration: underline; }

ol.changes { list-style: none; margin: 0 0 .5rem; padding: .35rem .9rem; }
ol.changes li {
  display: grid; grid-template-columns: 3.4rem 1fr auto; gap: .5rem;
  padding: .45rem 0; border-bottom: 1px solid var(--line); font-size: .9rem; align-items: baseline;
}
ol.changes li:last-child { border-bottom: none; }
ol.changes .day { color: var(--ink-mute); font-size: .8rem; font-variant-numeric: tabular-nums; }
ol.changes li.k-figure .what, ol.changes li.k-severity .what { font-weight: 600; }
ol.changes li.k-corroborated .what, ol.changes li.k-closed .what { color: var(--ink-soft); }
ol.changes li.k-casualty_update .what, ol.changes li.k-correction .what,
ol.changes li.k-escalation .what, ol.changes li.k-disputed .what { font-weight: 600; }
ol.changes li.k-confirmed .what, ol.changes li.k-status_change .what,
ol.changes li.k-location_added .what { color: var(--ink-soft); }
/* The kind of change, small and fixed-width in voice, so a column of them
   scans: CASUALTY UPDATE, CONFIRMED, CORRECTION. */
ol.changes .tag {
  display: inline-block; margin-right: .3rem;
  font-size: .64rem; font-weight: 700; letter-spacing: .06em; vertical-align: .08em;
  padding: .02rem .34rem; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); color: var(--ink-soft); background: var(--card);
}
ol.changes .tag.k-escalation, ol.changes .tag.k-casualty_update, ol.changes .tag.k-correction,
ol.changes .tag.k-disputed, ol.changes .tag.k-severity, ol.changes .tag.k-figure {
  border-color: var(--sev-4); color: var(--sev-4-ink); background: var(--sev-4-bg);
}
ol.changes .tag.k-new_information, ol.changes .tag.k-official_response,
ol.changes .tag.k-operational_change, ol.changes .tag.k-resolved {
  border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft);
}
ol.changes .chip.ai, .brief .chip.ai {
  font-size: .66rem; padding: 0 .3rem; font-weight: 650; letter-spacing: .04em;
  border-color: var(--accent); color: var(--accent-ink);
}
ol.changes .chip.unverified { font-size: .66rem; padding: 0 .3rem; }
details.more { margin: 0 0 .8rem; }
details.more > summary { cursor: pointer; font-size: .85rem; color: var(--accent); margin: .2rem 0 .5rem; }

.facts dl, .locations dl { margin: 0; }
.facts .fact, .locations dl > div {
  display: grid; grid-template-columns: minmax(8rem, 13rem) 1fr; gap: .2rem .8rem;
  padding: .45rem 0; border-bottom: 1px solid var(--line);
}
.facts .fact:last-child, .locations dl > div:last-child { border-bottom: none; }
.facts dt, .locations dt { color: var(--ink-soft); font-size: .86rem; }
.facts dd, .locations dd { margin: 0; font-size: .9rem; }
.facts .value { font-weight: 650; font-variant-numeric: tabular-nums; margin-right: .4rem; }
.facts .state-differs .value { color: var(--sev-3-ink); }
.facts .per, .locations .per, .related-stories .per { font-size: .8rem; color: var(--ink-mute); }
.facts q.said {
  display: block; margin-top: .15rem; padding: 0; border: 0; background: none;
  font-size: .8rem; color: var(--ink-soft); font-style: italic;
}
.facts .earlier { display: block; font-size: .8rem; color: var(--ink-mute); margin-top: .15rem; }
.facts .caveat, .locations .caveat { color: var(--ink-mute); font-size: .76rem; margin: .5rem 0 0; }

.verification .named { margin: .45rem 0 0; font-size: .9rem; }
.verification details.counted, .verification details.moved { margin-top: .6rem; }
.verification details > summary { cursor: pointer; font-size: .86rem; font-weight: 600; color: var(--ink-soft); }
.verification .meta-note { font-size: .8rem; color: var(--ink-mute); margin: .3rem 0; }

h2.section .order { margin-left: auto; font-size: .76rem; font-weight: 500; letter-spacing: 0; text-transform: none; }
h2.section .order + .count, h2.section .count + .order { margin-left: .6rem; }
ol.timeline { list-style: none; margin: 0 0 .5rem; padding: 0 0 0 .9rem; border-left: 2px solid var(--line-strong); }
ol.timeline li.tl { position: relative; padding: 0 0 .9rem .4rem; }
ol.timeline li.tl::before {
  content: ""; position: absolute; left: -1.28rem; top: .35rem;
  width: .6rem; height: .6rem; border-radius: 50%;
  background: var(--card); border: 2px solid var(--line-strong);
}
ol.timeline li.sev-3::before { border-color: var(--sev-3); }
ol.timeline li.sev-4::before { border-color: var(--sev-4); }
ol.timeline li.sev-5::before { border-color: var(--sev-5); }
ol.timeline .tl-meta { display: flex; flex-wrap: wrap; gap: .25rem .4rem; align-items: center; font-size: .78rem; color: var(--ink-mute); }
ol.timeline .tl-meta .who { color: var(--ink-soft); font-weight: 600; }
ol.timeline .tl-meta .chip { font-size: .68rem; padding: 0 .35rem; }
ol.timeline .tl-title { display: block; margin: .15rem 0; color: var(--ink); font-weight: 570; text-decoration: none; line-height: 1.35; }
ol.timeline .tl-title:hover { color: var(--accent); text-decoration: underline; }
ol.timeline .tl-summary { margin: .1rem 0 .2rem; font-size: .86rem; color: var(--ink-soft); }
ol.timeline .tl-links { font-size: .78rem; }
ol.timeline .tl-links a { color: var(--ink-mute); }
ol.timeline li.dup { opacity: .62; }
ol.timeline li.dup .tl-title { font-weight: 450; }
ol.timeline details.copies { margin-top: .25rem; font-size: .8rem; }
ol.timeline details.copies > summary { cursor: pointer; color: var(--ink-mute); }
ol.timeline details.copies .reports { margin: .3rem 0 0; padding-left: .9rem; }

.locations .maplink { margin: .6rem 0 0; font-size: .85rem; }
details.inline { display: inline; }
details.inline > summary { display: inline; cursor: pointer; color: var(--accent); }

.related-stories.card { padding: .2rem .9rem; }
.related-stories .chip.rel-kind { font-size: .7rem; font-weight: 600; color: var(--ink-soft); }
.related-stories .per { flex-basis: 100%; }

.note.merged-via { margin: 0 0 .7rem; }
details.how-built > summary { cursor: pointer; }
details.how-built p { margin: .4rem 0 0; }

@media (max-width: 560px) {
  .story-head h1 { font-size: 1.12rem; }
  .facts .fact, .locations dl > div { grid-template-columns: 1fr; gap: .1rem; }
  ol.changes li { grid-template-columns: 3rem 1fr; }
  ol.changes li .cite { grid-column: 2; }
  h2.section { flex-wrap: wrap; }
}

/* ---- Hazards: one card per hazard ---------------------------------------
   Built for a glance on a phone: what, where, how it stands, what is happening,
   what changed last. Status is a word as well as a colour, so it reads without
   colour too. See app/web/templates/_hazard.html and hazard.html. */
.hazard-disclaimer { margin: 0 0 .8rem; font-size: .8rem; }
.hazard-list { display: grid; gap: .6rem; }
.card.hazard h3 { font-size: 1.02rem; }
.card.hazard h3 .what { text-transform: uppercase; letter-spacing: .03em; }
.card.hazard .latest { margin: .2rem 0 .45rem; font-size: .88rem; color: var(--ink); line-height: 1.45; }
.card.hazard .latest b { display: inline-block; margin-right: .3rem; font-size: .72rem;
  text-transform: uppercase; letter-spacing: .05em; color: var(--accent); }
.card.hazard .when, .hazard-head .when, .latest-card .when, .notices .when {
  color: var(--ink-mute); font-size: .8rem; font-variant-numeric: tabular-nums; }
.card.hazard .official { display: flex; gap: .35rem; align-items: baseline; margin: 0 0 .45rem;
  font-size: .84rem; color: var(--ink-soft); }
.card.hazard .official .icon { flex: none; width: .95em; height: .95em; }
.card.hazard ul.impacts { display: flex; flex-wrap: wrap; gap: .3rem; list-style: none;
  margin: 0 0 .45rem; padding: 0; }
.card.hazard ul.impacts li { font-size: .76rem; padding: .08rem .45rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink-soft); }
.card.hazard ul.impacts li.more { border-style: dashed; }
.card.hazard.st-resolved { opacity: .78; }

.chip.htype { font-weight: 650; }
.chip.hstatus { font-weight: 650; }
.chip.hs-emerging, .chip.hs-active { border-color: var(--bad); color: var(--bad); }
.chip.hs-improving { border-color: var(--ok); color: var(--ok); }
.chip.hs-monitoring { border-color: var(--warn); color: var(--warn); }
.chip.hs-resolved, .chip.hs-unknown { color: var(--ink-mute); }
.chip.evidence { color: var(--ink-soft); }
.chip.ev-unverified { border-color: var(--warn); color: var(--warn); }

.hazard-head .status-reason { margin: .45rem 0 0; font-size: .86rem; color: var(--ink-soft); }
.situation .line { margin: .45rem 0 0; font-size: .92rem; }
.latest-card, .notices, .impacts-card { padding: .7rem .95rem; }
.latest-card p { margin: 0; line-height: 1.5; }
ul.plain { list-style: none; margin: 0; padding: 0; }
ul.plain > li { padding: .45rem 0; border-bottom: 1px solid var(--line); font-size: .88rem; }
ul.plain > li:last-child { border-bottom: none; }
.notices li.reported { color: var(--ink-soft); }
.impacts-card h4 { margin: .3rem 0 .1rem; font-size: .8rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-mute); }
.impacts-card .quote { color: var(--ink-soft); font-size: .82rem; }
.hazard-sources-card { padding: .2rem .9rem; }
.hazard-sources li { display: flex; flex-wrap: wrap; gap: .3rem .45rem; align-items: baseline; }

/* ---- The current hazard picture (app/web/templates/_physical_hazard.html) --
   Authoritative records and related reporting sit in two separate blocks, so
   an observation and a news report never read as one kind of evidence. */
.picture-lead { margin: 0 0 .35rem; font-size: .95rem; line-height: 1.45; }
.card.physical .when-line { display: flex; gap: .3rem; align-items: center; margin: 0 0 .15rem;
  font-size: .82rem; color: var(--ink-mute); }
.card.physical .when-line .icon { width: .9em; height: .9em; }
.card.physical .status-line { margin: 0 0 .5rem; font-size: .84rem; color: var(--ink-soft); }
.ph-block { margin: 0 0 .55rem; padding: .45rem .6rem; border-radius: 6px; border: 1px solid var(--line); }
.ph-block.reported { border-style: dashed; }
.ph-block h4 { margin: 0 0 .25rem; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-mute); }
.ph-block .caption { margin: .15rem 0 .3rem; }
.ph-block h4.sub { margin-top: .4rem; }
ul.ph-list { list-style: none; margin: 0; padding: 0; }
ul.ph-list li { padding: .2rem 0; font-size: .86rem; line-height: 1.45; overflow-wrap: anywhere; }
ul.ph-list .when { color: var(--ink-mute); font-size: .78rem; }
.chip.role { font-size: .66rem; padding: 0 .35rem; text-transform: uppercase; letter-spacing: .04em; }
.chip.ph-active { border-color: var(--bad); color: var(--bad); }
.chip.ph-recent { border-color: var(--warn); color: var(--warn); }
.chip.ph-resolved, .chip.ph-expired { color: var(--ink-mute); }
.card.physical.st-resolved, .card.physical.st-expired { opacity: .78; }
.hazard-map { margin: .8rem 0; }
.map .map-hz { fill: var(--card); stroke-width: 2.2; stroke: var(--ink-mute); }
.map .map-hz.quake { fill: none; }
.map .map-hz.ph-active { stroke: var(--bad); }
.map .map-hz.ph-recent { stroke: var(--warn); }
.map .map-hz.ph-resolved, .map .map-hz.ph-expired { stroke: var(--line-strong); stroke-dasharray: 3 2; }
.map a:hover .map-hz { stroke-width: 3.2; }
.legend i.hz-key { background: transparent; border: 2px solid var(--ink-mute); }
.legend i.hz-key.quake { border-radius: 50%; }
.legend i.hz-key.alert { transform: rotate(45deg) scale(.8); }
.legend i.hz-key.current { border-color: var(--warn); }
.legend i.hz-key.past { border-style: dashed; border-color: var(--line-strong); }
.reported-head { margin-top: 1.4rem; }

details.hazard-resolved > summary { list-style: none; cursor: pointer; }
details.hazard-resolved > summary::-webkit-details-marker { display: none; }
details.feed-records { margin: .35rem 0; }
details.feed-records > summary { cursor: pointer; padding: .45rem 0; font-weight: 600; font-size: .9rem; }
ul.record-list { list-style: none; margin: 0; padding: 0; }
ul.record-list li { display: flex; flex-wrap: wrap; gap: .4rem; align-items: baseline;
  padding: .35rem 0; border-bottom: 1px solid var(--line); font-size: .86rem; }
ul.record-list .when { color: var(--ink-mute); font-size: .78rem; }

/* ---- Briefs (app/web/templates/briefings.html) --------------------------
   Read on a phone, top to bottom: coverage, the picture, what changed, what
   to watch. Type does the work — no cards, no images, one column. Sources sit
   behind a disclosure so the reading line stays short. */
.brief-tabs { display: flex; gap: .3rem; flex-wrap: wrap; margin: 0 0 .9rem; }
.brief-tabs a { padding: .3rem .7rem; border: 1px solid var(--line-strong); border-radius: 999px;
  font-size: .82rem; color: var(--ink-soft); text-decoration: none; }
.brief-tabs a.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.brief { max-width: 46rem; }
.brief-observation, .brief-reported { margin: .25rem 0; font-size: .88rem; }
.brief-observation b, .brief-reported b { font-size: .72rem; text-transform: uppercase;
  letter-spacing: .05em; color: var(--ink-mute); margin-right: .25rem; }
.brief-reported ul { margin: .15rem 0 0; padding-left: 1.1rem; }
.brief-head h1 { margin: .1rem 0 .35rem; font-size: 1.5rem; line-height: 1.2; letter-spacing: -.01em; }
.brief-kicker { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-mute); }
.brief-coverage { display: flex; gap: .4rem; align-items: baseline; margin: 0; font-size: .92rem; }
.brief-coverage .icon { width: 1em; height: 1em; flex: none; align-self: center; }
.brief-meta { margin: .25rem 0 .6rem; font-size: .8rem; }
.brief-nav { display: flex; justify-content: space-between; align-items: center; gap: .5rem;
  flex-wrap: wrap; margin: .5rem 0 1rem; font-size: .86rem; }
.brief-date { display: flex; gap: .3rem; align-items: center; }
.brief-date input { font: inherit; padding: .2rem .35rem; }
.brief-date button { font: inherit; padding: .2rem .6rem; }
.brief-section { margin: 1.3rem 0 0; }
.brief-section > h2 { margin: 0 0 .5rem; padding-bottom: .25rem; border-bottom: 2px solid var(--ink);
  font-size: .8rem; font-weight: 750; text-transform: uppercase; letter-spacing: .07em; }
.brief-overall { margin: 0 0 .45rem; font-size: 1rem; line-height: 1.55; }
.brief-list { margin: 0; padding: 0 0 0 1.3rem; }
ul.brief-list { list-style: none; padding-left: 0; }
.brief-dev { margin: 0 0 1rem; }
.brief-dev-head { display: flex; flex-wrap: wrap; gap: .25rem .45rem; align-items: baseline; }
.brief-dev-title { font-weight: 680; color: var(--ink); text-decoration: none; font-size: 1rem; }
.brief-dev-title:hover { color: var(--accent); text-decoration: underline; }
.brief-where { font-size: .78rem; }
.brief-state { font-size: .64rem; font-weight: 750; letter-spacing: .06em; padding: .05rem .35rem;
  border-radius: 3px; border: 1px solid var(--line-strong); color: var(--ink-soft); }
.brief-state.st-new { border-color: var(--accent); color: var(--accent); }
.brief-state.st-updated { border-color: var(--warn); color: var(--warn); }
.brief-state.st-resolved { border-color: var(--ok); color: var(--ok); }
.brief-summary { margin: .25rem 0 .3rem; line-height: 1.5; font-size: .94rem; }
.brief-changed { margin: .2rem 0 .3rem; padding: .35rem .6rem; background: var(--accent-soft);
  border-left: 3px solid var(--accent); font-size: .9rem; }
.brief-changed b, .brief-previously b { font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; }
.brief-changed ul { margin: .15rem 0 0; padding-left: 1.1rem; }
.brief-changed li { margin: .1rem 0; line-height: 1.45; }
.brief-previously { margin: .2rem 0; font-size: .84rem; color: var(--ink-mute); }
.brief-cite { font-size: .74rem; color: var(--ink-mute); white-space: nowrap; }
.brief-cite::before { content: "— "; }
.brief-sources { margin: .2rem 0 0; font-size: .8rem; }
.brief-sources > summary { cursor: pointer; color: var(--ink-mute); }
.brief-sources ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.brief-sources li { margin: .15rem 0; }
.brief-compact { list-style: none; margin: 0; padding: 0; }
.brief-compact > li { padding: .45rem 0; border-bottom: 1px solid var(--line); font-size: .9rem; line-height: 1.45; }
.brief-compact > li:last-child { border-bottom: none; }
.brief-line { margin-top: .1rem; font-size: .86rem; }
.brief-more { margin: .3rem 0 0; font-size: .82rem; }
.brief-foot { margin: 1.6rem 0 0; padding-top: .6rem; border-top: 1px solid var(--line); font-size: .78rem; }
.brief-foot p { margin: .3rem 0; }
.brief-archive { list-style: none; margin: 0; padding: 0; }
.brief-archive li { padding: .4rem 0; border-bottom: 1px solid var(--line); font-size: .86rem; }
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
@media (max-width: 560px) {
  .brief-head h1 { font-size: 1.3rem; }
  .brief-cite { white-space: normal; }
}

/* ------------------------------------------------------------ situations -- */
/* Several distinct stories that make one continuing issue — see
   app/pipeline/situations.py. Built from the hazard card's pieces. */
.situation-list { display: grid; gap: .75rem; }
.card.situation-card h3 { margin: .15rem 0 .3rem; }
.situation-where { flex-wrap: wrap; }
.situation-card .latest { margin: .2rem 0 .45rem; font-size: .86rem; color: var(--ink-soft); }
.situation-card .latest .when { color: var(--ink-mute); font-size: .8rem; margin-right: .2rem; }
.sit-points { margin: .3rem 0 .4rem; }
.sit-points > b { display: block; font-size: .68rem; text-transform: uppercase;
  letter-spacing: .06em; color: var(--ink-mute); margin-bottom: .2rem; }
.situation-card ul.impacts, .sit-points ul.impacts { display: flex; flex-wrap: wrap; gap: .3rem;
  list-style: none; margin: 0; padding: 0; }
.situation-card ul.impacts li { font-size: .76rem; padding: .08rem .45rem; border-radius: 999px;
  border: 1px solid var(--line); color: var(--ink-soft); }
.situation-card ul.impacts.watch li { border-style: dashed; }
.card.situation-card.ss-dormant, .card.situation-card.ss-resolved { opacity: .8; }

.chip.sstatus { font-weight: 650; }
.chip.ss-developing, .chip.ss-active { border-color: var(--bad); color: var(--bad); }
.chip.ss-stabilising { border-color: var(--warn); color: var(--warn); }
.chip.ss-resolved { border-color: var(--ok); color: var(--ok); }
.chip.ss-dormant { color: var(--ink-mute); }
.chip.review.rv-proposed { border-style: dashed; color: var(--ink-soft); }
.chip.review.rv-rejected { color: var(--ink-mute); text-decoration: line-through; }
.chip.assoc { color: var(--ink-soft); }
.chip.assoc.as-medium, .chip.assoc.as-low { border-style: dashed; }
.chip.obs { border-color: var(--accent); color: var(--accent); }

.situation-head .kicker { margin: 0 0 .2rem; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--ink-mute); display: flex; gap: .3rem; align-items: center; }
.situation-head .kicker .icon { width: 1em; height: 1em; }
.situation-head .status-reason { margin: .45rem 0 0; font-size: .86rem; color: var(--ink-soft); }
.sit-grid > section { min-width: 0; }
.sit-timeline li.o-change .what { color: var(--ink-soft); }
.card.sit-story { padding: .7rem .95rem; margin-bottom: .6rem; }
.card.sit-story h3 { margin: .25rem 0 .2rem; font-size: 1rem; }
.sit-story .evidence-detail .quote { color: var(--ink-soft); font-size: .82rem; }
.sit-story .inline-form { margin: .4rem 0 0; }
.sit-story .inline-form button { font-size: .76rem; padding: .15rem .5rem; }
.related-places { display: flex; flex-wrap: wrap; gap: .3rem; }
.review-card .review-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .6rem; }
.review-card .review-actions form { margin: 0; }
.review-card .rename { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.review-card .rename input { flex: 1 1 14rem; min-width: 0; font: inherit; font-size: .87rem;
  padding: .34rem .5rem; color: var(--ink); background: var(--bg-sunken);
  border: 1px solid var(--line); border-radius: var(--radius-sm); }
.overview-situations .situation-list { gap: .5rem; }
.brief-situation { margin: .1rem 0 .15rem; font-size: .8rem; color: var(--ink-mute);
  display: flex; gap: .3rem; align-items: center; flex-wrap: wrap; }
.brief-situation .icon { width: 1em; height: 1em; }

/* -------------------------------------------------------------- patterns -- */
/* Several distinct events of one kind in a period — see
   app/pipeline/patterns.py. Built from the situation card's pieces. */
.chip.pattern-kind { display: inline-flex; gap: .25rem; align-items: center; }
.chip.pattern-kind .icon { width: 1em; height: 1em; }
.chip.evidence.ev-strong { border-color: var(--ok); color: var(--ok); font-weight: 650; }
.chip.evidence.ev-moderate { border-style: dashed; color: var(--ink-soft); }
.pattern-card .pattern-counts { margin: .1rem 0 .3rem; }
.pattern-card.lapsed { opacity: .75; }
.pattern-card .pattern-why { overflow-wrap: anywhere; }
.pattern-unit.left-out { opacity: .8; }
.pattern-unit .evidence-detail summary { overflow-wrap: anywhere; }
.overview-patterns .situation-list { gap: .5rem; }
.overview-patterns .more-link { margin: .4rem 0 0; font-size: .82rem; }
.brief-pattern-meta { font-size: .8rem; color: var(--ink-mute); }

/* --------------------------------------------------------- reader notes -- */
/* What a reader of the briefing said back, by replying to the email — see
   app/pipeline/replies.py. On a story or hazard page, and on /system/replies.
   Their words, kept visibly apart from the reports. */
.reader-notes .notes, .reply .notes { list-style: none; margin: 0; padding: 0; }
.reader-notes .notes li, .reply .notes li {
  padding: .5rem 0; border-bottom: 1px solid var(--line);
}
.reader-notes .notes li:last-child, .reply .notes li:last-child { border-bottom: none; }
.reader-notes .said, .reply .said {
  margin: 0 0 .2rem; font-size: .95rem; line-height: 1.45;
  padding-left: .7rem; border-left: 3px solid var(--accent); overflow-wrap: anywhere;
}
.reader-notes .per, .reply .per { font-size: .8rem; color: var(--ink-mute); margin: 0; }
.reader-notes .who, .reply .who { font-weight: 600; color: var(--ink-soft); }
.reader-notes .caveat { color: var(--ink-mute); font-size: .76rem; margin: .5rem 0 0; }

.replies { list-style: none; margin: 0 0 1rem; padding: 0; display: grid; gap: .6rem; }
.reply .glance { display: flex; flex-wrap: wrap; gap: .35rem .6rem; align-items: center; }
.reply .glance .when { font-size: .8rem; color: var(--ink-mute); display: inline-flex;
  gap: .25rem; align-items: center; }
.reply .glance .when .icon { width: 1em; height: 1em; }
.reply .subject { margin: .35rem 0 .15rem; font-weight: 550; overflow-wrap: anywhere; }
.reply .chip.ok { border-color: var(--ok); color: var(--ok); font-weight: 600; }
.reply .chip.counted { color: var(--ink-mute); }
.reply .raw {
  margin: .4rem 0 0; padding: .5rem .7rem; font-size: .78rem; line-height: 1.4;
  white-space: pre-wrap; overflow-wrap: anywhere; background: var(--bg);
  border: 1px solid var(--line); border-radius: var(--radius); max-height: 24rem; overflow: auto;
}

/* ---- the archive's context (app/web/templates/_archive.html) ------------- */
.archive h4 { margin: 1rem 0 .4rem; font-size: .9rem; }
.archive h4 .meta, .archive .meta { font-weight: 400; color: var(--ink-mute); font-size: .8rem; }
.archive ul { list-style: none; margin: 0; padding: 0; }
.archive ul > li { padding: .45rem 0; overflow-wrap: anywhere; }
.archive ul > li + li { border-top: 1px solid var(--line); }
.archive .explain { margin: .25rem 0 0; font-size: .85rem; color: var(--ink-soft); }
.archive .archive-evidence { margin-top: .3rem; }
.archive .archive-evidence li { padding: .15rem 0; border: 0; font-size: .8rem; }
.archive .archive-evidence .when, .archive .archive-evidence .outlet { color: var(--ink-mute); margin-right: .3rem; }
.archive .withheld { font-style: italic; color: var(--ink-mute); }
.archive .archive-episodes .name { font-weight: 600; margin-right: .4rem; }
.archive .archive-episodes .when { color: var(--ink-mute); font-size: .85rem; }
.archive .archive-episodes .meta { display: block; }
.archive .archive-limits, .archive .archive-stamp { font-size: .75rem; }
.archive details { margin-top: .5rem; }
.archive .chip.status { margin-right: .3rem; }
.brief-archive, .brief-line.brief-archive { color: var(--ink-soft); font-size: .88rem; }
.brief-archive ul { margin: .2rem 0 0; }

/* ---- a client's dashboard (app/web/templates/client.html) ---------------- */
/* One page, read on a phone. The label is the first thing under the masthead
   so nobody mistakes a beta for the finished thing. */
.client-label {
  margin: 0; padding: .5rem 1rem; text-align: center;
  font-size: .85rem; font-weight: 600;
  background: var(--accent-soft); color: var(--accent-ink);
  border-bottom: 1px solid var(--line);
}
/* The masthead is sticky, so a section jumped to must stop below it. */
body.client h2.section { scroll-margin-top: 4.5rem; }
.client-jump { margin: 0 0 1rem; }
.client-jump .chip { min-height: 44px; display: inline-flex; align-items: center; }
.client-glance { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.client-group {
  display: flex; align-items: center; flex-wrap: wrap; gap: .1rem .4rem;
  margin: 1.1rem 0 .5rem; font-size: .95rem; font-weight: 650;
}
.client-group .icon { width: 1em; height: 1em; color: var(--ink-mute); }
.client-group .count {
  margin-left: auto; font-size: .82rem; font-weight: 500; color: var(--ink-mute);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.client-none { margin: 0 0 .6rem; font-size: .88rem; color: var(--ink-mute); }
.client-changes { list-style: none; margin: 0; padding: 0; font-size: .9rem; }
.client-changes li { padding: .5rem 0; overflow-wrap: anywhere; }
.client-changes li + li { border-top: 1px solid var(--line); }
.client-changes b { display: block; font-size: .78rem; color: var(--ink-mute); font-weight: 600; }
.client-changes span { display: block; }

/* ---- a client's replies and reports (core app only) ---------------------- */
.client-sitreps { list-style: none; margin: 0; padding: 0; }
.client-sitreps li { padding: .5rem 0; overflow-wrap: anywhere; }
.client-sitreps li + li { border-top: 1px solid var(--line); }
.client-sitreps .meta { display: block; }
/* The email as it was sent, inside a page: a long address in a headline
   must wrap rather than push the page sideways on a phone. */
.client-sitrep-body { overflow-wrap: anywhere; overflow-x: auto; }
