/* ===========================================================================
   European Data Project — terminal design system

   Reference point is a trading terminal: black ground, amber chrome, dense
   tabular data, hairline rules, no rounded corners, no shadows. Everything is
   information; nothing is decoration.

   One deliberate departure from the trading-floor convention: rising values
   are NOT green and falling ones are NOT red. On a security monitor those
   colors assert a verdict the data doesn't support — a rising casualty count
   is not "good news in green". Movement is shown in a hot/cool pair and
   labelled "rising" / "falling", leaving the judgement to the reader.
   =========================================================================== */

:root {
  --void:    #07090d;   /* page ground */
  --panel:   #0d1117;   /* panel fill */
  --raise:   #121821;   /* header strips inside panels */
  --rule:    #1c232e;   /* hairlines */
  --rule-lt: #2a3441;   /* emphasised hairlines */

  --amber:   #ffa028;   /* terminal chrome: marks, active state, peaks */
  --text:    #c5cdd8;   /* primary reading color */
  --bright:  #eef2f7;   /* figures */
  --logo-ink: #ffffff;
  /* Raised 8 Oct 2026: dim was 3.9:1 and faint 2.1:1 on the panel, both
     below AA, and both carry real text. Now 6.3:1 and 4.5:1. */
  --dim:     #8b96a6;   /* labels, metadata */
  --faint:   #727d8d;   /* sources, disabled, empty states */

  --rising:  #ff7a5c;   /* movement up — not "bad" */
  --falling: #56a0cf;   /* movement down — not "good" */

  --ukraine:   #e8543f;
  --accession: #4f9ec4;
  --energy:    #3fa89b;
  --conflict:  #c96f4a;
  --migration: #4d94c4;
  --cyber:     #9b7cc8;
  --defense:   #7fa05f;
  --elections: #d4b24c;
  --support:   #d08a4e;   /* coffee */

  /* Mono carries all chrome, labels and figures; the serif carries prose.
     The caveat text under each panel is the part of this design doing the
     most work, and a serif is measurably easier to read at that length —
     which matters, because those caveats are what keep the numbers honest. */
  --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, monospace;
  --prose: 'Newsreader', Georgia, 'Times New Roman', serif;

  --rail-w: 198px;

  /* Content width and chart height scale with the viewport at the widescreen
     breakpoints near the bottom of this file. Everything that used to hard-code
     1240px reads these instead, so one change moves the whole page. */
  --content-max: 1240px;
  --chart-h: 210px;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  body, .panel, .tick, .rail { transition: none !important; }
}

body {
  margin: 0;
  background: var(--void);
  transition: background-color 140ms ease, color 140ms ease;
  color: var(--text);
  font-family: var(--mono);
  font-size: 14.5px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

.shell { display: flex; min-height: 100vh; }


/* ---------------------------------------------------------------------------
   Light theme.

   Not an inversion of the dark one. A terminal in daylight stops being a
   terminal, so the light reference is printed financial press instead: cool
   paper ground, white panels so charts sit on a clean field, ink-dark text,
   and the amber pulled right down to a burnt tone that actually holds
   contrast on white. Accents are re-chosen for a light ground rather than
   lightened, and mirror THEMES in api/charts.py.
   --------------------------------------------------------------------------- */
:root[data-theme="light"] {
  --logo-ink: #06204f;
  /* Light v2. The first attempt inverted the dark palette and it read worse,
     for reasons that turned out not to be contrast — measured, light actually
     had HIGHER text contrast than dark. Three real faults:

       1. The amber stopped being a signal. #9a5b00 on white is a muddy brown,
          so the one colour tying the design together (rail mark, peak
          markers, feed timestamps, leader bar) lost all presence.
       2. Pure white panels with near-black text were too harsh for a dense
          page you stare at. Editorial light designs use off-white grounds and
          softer ink, not maximum contrast.
       3. Chart accents had been darkened for contrast and went muddy rather
          than crisp.

     So: warm paper rather than cool white, which also suits the Newsreader
     serif far better than a neutral grey did; softer ink; and a saturated
     burnt orange that still clears AA at 4.6:1. */
  --void:    #e6e1d6;   /* paper field */
  --panel:   #f8f5ee;   /* warm off-white, not #fff */
  --raise:   #eeeae0;   /* header strips */
  --rule:    #d4cec0;   /* hairlines, 1.44:1 against panel */
  --rule-lt: #b5ad9a;

  /* Light v3 (8 Oct 2026): text darkened after it read as grey on phones.
     Measured on the panel ground: text 16.4:1, dim 9.7:1, faint 7.5:1 (was
     3.7:1, failing AA, and faint carries real text: sources, tick metadata),
     amber 6.0:1. */
  --amber:   #9a4508;
  --text:    #15181c;
  --bright:  #08090b;
  --dim:     #3a3f46;
  --faint:   #4b5057;

  --rising:  #b5390f;
  --falling: #1d6486;

  /* Saturated mid-tones rather than darkened versions of the dark palette —
     darkening is what made these muddy. */
  --ukraine:   #c9402c;
  --accession: #2079a8;
  --conflict:  #a8552b;
  --cyber:     #6d4fa8;
  --defense:   #4f7031;
  --elections: #8a6d12;
  --support:   #8a4f1f;
}

/* Subpixel-off antialiasing thins dark text on a light ground; let the
   platform default render it at full weight in the light theme. */
:root[data-theme="light"] body { -webkit-font-smoothing: auto; }

/* ------------------------------------------------------------ theme btn --- */
/* A sliding pill rather than a labelled button. Flat, not the glossy 3D kind
   — a bevelled candy switch would read as a different product bolted on. The
   track borrows the panel border and the knob the amber accent, so it looks
   like it was always part of the rail. */
.theme-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 18px 12px;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--dim);
  font-family: var(--mono);
  font-size: 13.5px;
  letter-spacing: 0.03em;
  cursor: pointer;
  width: calc(100% - 36px);
  justify-content: space-between;
}
.theme-toggle:hover { color: var(--text); }
.theme-toggle:hover .theme-track { border-color: var(--amber); }
.theme-toggle:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }

/* The track. Sized in ems so it scales with the label rather than drifting
   out of proportion when the type scale changes. */
.theme-track {
  position: relative;
  flex: 0 0 auto;
  width: 34px;
  height: 18px;
  border-radius: 999px;
  border: 1px solid var(--rule-lt);
  background: var(--sunk);
  transition: background 140ms ease, border-color 140ms ease;
}
.theme-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--dim);
  transition: transform 160ms cubic-bezier(.4,.0,.2,1), background 140ms ease;
}
/* Light mode = knob travelled right and lit. Driven off aria-pressed so the
   accessible state and the visual state cannot disagree. */
.theme-toggle[aria-pressed="true"] .theme-track {
  background: color-mix(in srgb, var(--amber) 22%, transparent);
  border-color: var(--amber);
}
.theme-toggle[aria-pressed="true"] .theme-knob {
  transform: translateX(16px);
  background: var(--amber);
}
.theme-toggle-state {
  color: var(--amber);
  min-width: 4.5ch;
  text-align: right;
}

@media (prefers-reduced-motion: reduce) {
  .theme-track, .theme-knob { transition: none; }
}

/* ---------------------------------------------------------------- rail --- */
.rail {
  width: var(--rail-w);
  flex: 0 0 var(--rail-w);
  border-right: 1px solid var(--rule);
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 18px 0;
  background: var(--void);
}

/* Home button: the full logo lockup, linking back to the default tab. */
.rail-mark {
  display: block;
  padding: 2px 16px 18px;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 14px;
  text-decoration: none;
  border-radius: 2px;
  /* The E and the EUROPEAN / PROJECT lines draw in currentColor, so the one
     inline logo follows the theme: white on dark, brand navy on light. The
     blue mark and DATA keep their own gradient in both. */
  color: var(--logo-ink);
}
.rail-logo-full { display: block; width: 100%; max-width: 196px; height: auto; }
.rail-logo-mark { display: none; }
.rail-mark:hover svg { opacity: 0.9; }
.rail-mark:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }

.rail-nav { display: flex; flex-direction: column; }

.rail-link {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 16px;
  color: var(--dim);
  text-decoration: none;
  font-size: 14px;
  border-left: 2px solid transparent;
}
.rail-link:hover { color: var(--text); background: var(--panel); }

/* Outline icons, stroked with currentColor so they inherit the link colour —
   dim when inactive, the tab's own accent when active — with no per-state
   fill rules and nothing to update if a palette changes. */
.rail-icon {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.75;
}
/* Solid-filled icons (the star ring) need fill rather than stroke; everything
   else in the set is stroked. Kept as a modifier so the default treatment
   stays one rule. */
.rail-icon-solid {
  stroke: none;
  fill: currentColor;
}

.rail-link:hover .rail-icon { opacity: 1; }

/* Active icon takes the tab's own colour while the label stays high-contrast.
   That way the colour carries information — which domain you are in — rather
   than being decoration, and it matches the accent on that tab's charts. */
.rail-link.active .rail-icon {
  opacity: 1;
  stroke: var(--tab, var(--amber));
}
.rail-link.active .rail-icon-solid {
  stroke: none;
  fill: var(--tab, var(--amber));
}
.rail-link.active { color: var(--bright); background: var(--panel); }
.rail-link.active { border-left-color: var(--tab, var(--amber)); }
/* One variable per tab, so the border, the icon and anything added later all
   read from the same source instead of repeating hex values per state. */
.rail-link[data-accent="ukraine"]   { --tab: var(--ukraine); }
.rail-link[data-accent="accession"] { --tab: var(--accession); }
.rail-link[data-accent="energy"]    { --tab: var(--energy); }
.rail-link[data-accent="conflict"]  { --tab: var(--conflict); }
.rail-link[data-accent="migration"] { --tab: var(--migration); }
.rail-link[data-accent="cyber"]     { --tab: var(--cyber); }
.rail-link[data-accent="defense"]   { --tab: var(--defense); }
.rail-link[data-accent="elections"] { --tab: var(--elections); }
.rail-link[data-accent="support"]   { --tab: var(--support); }
.rail-link:hover { border-left-color: var(--tab, transparent); }

.rail-foot {
  margin-top: auto;
  padding: 14px 18px 0;
  border-top: 1px solid var(--rule);
  font-size: 12.5px;
  color: var(--dim);
}
.rail-clock {
  color: var(--amber);
  font-variant-numeric: tabular-nums;
  font-size: 15.5px;
  letter-spacing: 0.04em;
}
.rail-disclaimer {
  font-family: var(--prose);
  font-size: 12px;
  line-height: 1.45;
  color: var(--faint);
  margin: 10px 0 0;
  padding-top: 9px;
  border-top: 1px solid var(--rule);
}

.rail-fetch {
  margin-top: 3px;
  font-size: 11.5px;
  color: var(--faint);
  /* Wrapped to two lines once the rail got its final width; clip instead. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------------------------------------------------------------- main --- */
.main { flex: 1; min-width: 0; }

/* -------------------------------------------------------------- ticker --- */
.ticker {
  display: flex;
  border-bottom: 1px solid var(--rule-lt);
  background: var(--panel);
  overflow-x: auto;
  scrollbar-width: none;
  /* Snap so a swipe lands on a whole figure rather than halfway through one. */
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
.tick { scroll-snap-align: start; }

/* The ticker scrolls sideways with nothing to say so. This fades the right
   edge, which is the cheapest honest signal that more exists off-screen. */
.ticker-wrap { position: relative; }
.ticker-wrap::after {
  content: '';
  position: absolute;
  right: 0; top: 0; bottom: 0;
  width: 42px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--panel));
}
@media (min-width: 1100px) {
  .ticker-wrap::after { display: none; }
}
.ticker::-webkit-scrollbar { display: none; }
.ticker-empty { padding: 12px 20px; color: var(--faint); font-size: 13px; }

.tick {
  flex: 1 0 auto;
  min-width: 190px;
  padding: 11px 20px;
  border-right: 1px solid var(--rule);
}
.tick-label {
  font-size: 11.5px;
  color: var(--dim);
  margin-bottom: 4px;
  white-space: nowrap;
}
.tick-row { display: flex; align-items: baseline; gap: 9px; }
.tick-value {
  font-size: 21px;
  font-weight: 500;
  color: var(--bright);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.tick-value.na { color: var(--faint); font-size: 16.5px; }
.tick-delta { font-size: 13px; font-variant-numeric: tabular-nums; }
.tick-delta.rising  { color: var(--rising); }
.tick-delta.falling { color: var(--falling); }
.tick-delta.flat    { color: var(--dim); }
.tick-meta { font-size: 11.5px; color: var(--faint); margin-top: 3px; }

/* ------------------------------------------------------------ masthead --- */
.masthead {
  padding: 40px 28px 30px;
  border-bottom: 1px solid var(--rule);
  max-width: var(--content-max);
}
.masthead h1 {
  font-family: var(--mono);
  font-size: 27px;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--bright);
  margin: 0 0 12px;
}
.standfirst {
  font-family: var(--prose);
  font-size: 16.5px;
  line-height: 1.62;
  color: var(--text);
  max-width: 64ch;
  margin: 0;
}

/* --------------------------------------------------------------- block --- */
.block {
  padding: 30px 28px 38px;
  border-bottom: 1px solid var(--rule);
  scroll-margin-top: 10px;
}
.block-head { max-width: var(--content-max); margin-bottom: 20px; }
.block-head h2 {
  font-size: 16.5px;
  font-weight: 600;
  color: var(--bright);
  margin: 0 0 9px;
  padding-left: 11px;
  border-left: 3px solid var(--accent, var(--dim));
  letter-spacing: 0.01em;
}
.block[data-accent="ukraine"]   { --accent: var(--ukraine); }
.block[data-accent="accession"] { --accent: var(--accession); }
.block[data-accent="energy"]    { --accent: var(--energy); }
.block[data-accent="conflict"]  { --accent: var(--conflict); }
.block[data-accent="migration"] { --accent: var(--migration); }
.block[data-accent="cyber"]     { --accent: var(--cyber); }
.block[data-accent="defense"]   { --accent: var(--defense); }
.block[data-accent="elections"] { --accent: var(--elections); }
.block[data-accent="support"]   { --accent: var(--support); }

.block-intro {
  font-family: var(--prose);
  font-size: 16.5px;
  line-height: 1.64;
  color: var(--dim);
  max-width: 68ch;
  margin: 0;
}

/* ---------------------------------------------------------------- grid --- */
.grid { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule);
        max-width: var(--content-max); margin-bottom: 20px; }
.grid:last-child { margin-bottom: 0; }
.grid-1     { grid-template-columns: 1fr; }
.grid-2     { grid-template-columns: 1fr 1fr; }
.grid-quad  { grid-template-columns: 1fr 1fr; }   /* becomes 4-across on widescreen */
.grid-split { grid-template-columns: 1.5fr 1fr; }

/* --------------------------------------------------------------- panel --- */
.panel {
  background: var(--panel);
  display: flex; flex-direction: column; min-width: 0;
  transition: background-color 120ms ease;
}
/* Elevation by surface, not shadow — the convention that actually reads on a
   near-black ground. */
.panel:hover { background: var(--raise); }
.panel:hover .panel-head { background: var(--panel); }

.panel-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 9px 14px;
  background: var(--raise);
  border-bottom: 1px solid var(--rule);
}
.panel-title { font-size: 13.5px; font-weight: 500; color: var(--text); }
.panel-src { font-size: 11.5px; color: var(--faint); white-space: nowrap; }

/* Freshness badge. Amber for stale, hotter for a broken fetcher, muted for a
   source that simply isn't set up. Nothing at all when the data is current —
   a badge on every panel would stop meaning anything. */
.panel-flag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; letter-spacing: 0.02em;
  padding: 2px 6px; margin-left: 8px;
  border: 1px solid currentColor; white-space: nowrap;
}
.panel-flag::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: currentColor;
}
.panel-flag.stale    { color: var(--amber); }
.panel-flag.error    { color: var(--rising); }
.panel-flag.empty    { color: var(--faint); }
.panel-flag.inactive { color: var(--faint); }
.panel-head.flagged { border-bottom-color: currentColor; }

.panel-freshnote {
  font-family: var(--prose);
  font-size: 12.5px; line-height: 1.45;
  margin: 0 0 7px; padding-left: 9px;
  border-left: 2px solid var(--amber);
  color: var(--text);
}
.panel-freshnote.error { border-left-color: var(--rising); }
.panel-freshnote.empty,
.panel-freshnote.inactive { border-left-color: var(--faint); color: var(--dim); }

.panel-chart {
  height: var(--chart-h);
  padding: 8px 6px 4px;
  overflow: hidden;
  min-width: 0;
  position: relative;
}
/* Plotly's hover toolbar. Sits flush top-right inside the chart area and only
   appears on hover, so it costs nothing visually until wanted. */
.panel-chart .modebar {
  top: 2px !important;
  right: 4px !important;
}
.panel-chart .modebar-btn svg { vertical-align: middle; }
.panel-chart .modebar-group { background: transparent !important; }
/* Static, hand-written status card (no chart behind it): same surface as a
   panel, but not picked up by the panel renderer. */
.note-card { background: var(--panel); display: flex; flex-direction: column; min-width: 0; }

/* Map view presets (Full corridor / Balkans), above the map. */
.map-views { display: flex; gap: 6px; padding: 8px 12px 0; }
.map-views button {
  font: 500 11.5px var(--mono); letter-spacing: 0.02em; color: var(--dim);
  background: transparent; border: 1px solid var(--rule-lt); border-radius: 3px;
  padding: 4px 10px; cursor: pointer;
}
.map-views button:hover { color: var(--text); border-color: var(--amber); }
.map-views button[aria-pressed="true"] { color: var(--amber); border-color: var(--amber); }
.map-views button:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

.panel-chart > .js-plotly-plot,
.panel-chart > .js-plotly-plot .plot-container { width: 100% !important; height: 100% !important; }

.panel-context { padding: 10px 14px 13px; border-top: 1px solid var(--rule); }
.panel-measures {
  font-family: var(--prose);
  font-size: 15px;
  line-height: 1.52;
  color: var(--text);
  margin: 0 0 6px;
}
.panel-caveat {
  font-family: var(--prose);
  font-size: 14px;
  line-height: 1.54;
  color: var(--dim);
  margin: 0;
  padding-left: 9px;
  border-left: 1px solid var(--rule-lt);
}
.panel-cadence {
  font-size: 11.5px;
  color: var(--faint);
  margin-top: 7px;
}
.panel-error { padding: 28px 14px; color: var(--faint); font-size: 13px; }

/* ---------------------------------------------------------------- feed --- */
.feed { background: var(--panel); display: flex; flex-direction: column; min-width: 0; }
.feed-head {
  padding: 9px 14px;
  background: var(--raise);
  border-bottom: 1px solid var(--rule);
  font-size: 13.5px;
  color: var(--text);
}
.feed-list { flex: 1; overflow: hidden; }
.feed-item {
  display: block;
  padding: 9px 14px;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: inherit;
}
.feed-item:last-child { border-bottom: none; }
.feed-item:hover { background: var(--raise); }
.feed-time {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--amber);
  font-variant-numeric: tabular-nums;
  margin-bottom: 3px;
}

/* Bundled flag images, 120x90 with rounded corners and a hairline edge baked
   in (see RESUME.md), so white-heavy flags (Poland, Georgia) don't dissolve
   into the panel background and no CSS border is needed. */
.flag {
  display: inline-block;
  width: 18px;
  height: 13px;
  object-fit: cover;
  vertical-align: -1px;
  flex: 0 0 auto;
}
.feed-title {
  font-family: var(--prose);
  font-size: 15px;
  line-height: 1.44;
  color: var(--text);
}
.feed-empty { padding: 22px 14px; color: var(--faint); font-size: 13px; }

/* ------------------------------------------------------------- sources --- */
.sources-wrap {
  max-width: var(--content-max);
  overflow-x: auto;
  border: 1px solid var(--rule);
}
.sources {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-size: 13.5px;
}
.sources th {
  text-align: left;
  padding: 9px 14px;
  background: var(--raise);
  color: var(--dim);
  font-weight: 500;
  font-size: 12.5px;
  border-bottom: 1px solid var(--rule-lt);
}
.sources td {
  padding: 9px 14px;
  border-bottom: 1px solid var(--rule);
  color: var(--text);
}
.sources tbody tr:last-child td { border-bottom: none; }
.sources td:first-child { color: var(--amber); }
.sources-sub {
  font-family: var(--mono);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--amber);
  letter-spacing: 0.04em;
  margin: 26px 0 8px;
}

.sources-note {
  font-family: var(--prose);
  font-size: 15px;
  line-height: 1.62;
  color: var(--dim);
  max-width: 68ch;
  margin: 16px 0 0;
  padding-left: 11px;
  border-left: 1px solid var(--rule-lt);
}


/* ---------------------------------------------------------------------------
   Widescreen.

   The page previously capped at 1240px, which on a 2560px monitor left more
   than half the screen empty — the single most dated-looking thing about it.
   A terminal layout should get denser with room, not just wider, so the
   content cap, the chart height and the column count all step up together.
   The four Ukraine panels go 2x2 -> 4-across at 1900px, which is where a
   single row actually fits without the charts becoming letterbox slots.
   --------------------------------------------------------------------------- */
@media (min-width: 1500px) {
  :root { --content-max: 1440px; --chart-h: 230px; }
  .block { padding: 34px 34px 42px; }
  .masthead { padding: 46px 34px 32px; }
  .standfirst { font-size: 16.5px; }
}

@media (min-width: 1900px) {
  :root { --content-max: 1780px; --chart-h: 250px; --rail-w: 196px; }
  .grid-quad { grid-template-columns: repeat(4, 1fr); }
  .grid-split { grid-template-columns: 2fr 1fr; }
  .masthead h1 { font-size: 33px; }
  .tick { min-width: 220px; }
  .tick-value { font-size: 23px; }
}

@media (min-width: 2400px) {
  :root { --content-max: 2160px; --chart-h: 280px; }
  body { font-size: 15.5px; }
  .block { padding: 40px 44px 50px; }
  .masthead { padding: 56px 44px 38px; }
  .panel-measures { font-size: 16px; }
  .panel-caveat { font-size: 15px; }
  .feed-title { font-size: 16px; }
}

/* Beyond roughly 3000px, growing further stops helping — line lengths get
   unreadable — so the grid stays put and centres instead. */
@media (min-width: 3000px) {
  :root { --content-max: 2400px; }
  .grid, .block-head, .masthead, .sources-wrap { margin-left: auto; margin-right: auto; }
}

/* --------------------------------------------------------- responsive --- */
@media (max-width: 1020px) {
  .grid-2, .grid-quad, .grid-split { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  .shell { flex-direction: column; }

  /* Nav scrolls on one line instead of wrapping onto two. Seven tabs wrapped
     cost ~103px of a 844px viewport before any content appeared. */
  .rail {
    width: 100%; flex: none; height: auto; position: sticky; top: 0; z-index: 20;
    border-right: none; border-bottom: 1px solid var(--rule-lt);
    flex-direction: row; align-items: center; flex-wrap: nowrap;
    column-gap: 10px; row-gap: 0;
    padding: 7px 12px; overflow: visible;
    background: var(--void);
  }
  .rail-mark { padding: 0; border-bottom: none; margin: 0; }
  .rail-logo-full { display: none; }
  .rail-logo-mark { display: block; width: 34px; height: 34px; }
  .rail-nav {
    flex-direction: row; flex-wrap: nowrap; gap: 0;
    order: 3; width: 100%;
    overflow-x: auto; scrollbar-width: none;
    scroll-snap-type: x proximity;
    margin-top: 5px;
  }
  .rail-nav::-webkit-scrollbar { display: none; }
  .rail-link {
    padding: 5px 9px; border-left: none; border-bottom: 2px solid transparent;
    font-size: 13.5px; white-space: nowrap; scroll-snap-align: start;
    gap: 6px;
  }
  .rail-icon { width: 13px; height: 13px; }
  .rail-link.active { background: transparent; border-bottom-color: var(--tab, var(--amber)); }
  .rail-foot { margin: 0 0 0 auto; padding: 0; border-top: none; text-align: right; order: 2; }
  .rail-clock { font-size: 12.5px; }
  .rail-disclaimer { display: none; }
.rail-fetch { display: none; }
  .theme-toggle { width: auto; margin: 0; order: 1; padding: 3px 7px; font-size: 11.5px; }

  /* Prose was desktop-sized on a phone: masthead plus section intro ran about
     1,100px, so on some tabs you scrolled two full screens of text before
     reaching a single number. On a data site the numbers should be visible
     without scrolling to them. */
  .masthead { padding: 20px 14px 16px; }
  .masthead h1 { font-size: 21px; margin-bottom: 8px; }
  .standfirst { font-size: 15.5px; line-height: 1.5; max-width: none; }

  .block { padding: 18px 14px 22px; }
  .block-head { margin-bottom: 14px; }
  .block-head h2 { font-size: 15.5px; padding-left: 9px; }
  .block-intro { font-size: 15px; line-height: 1.5; max-width: none; }

  .tick { min-width: 172px; padding: 9px 14px; }
  .tick-value { font-size: 19px; }

  /* Taller than desktop, not shorter: a phone has vertical room to spare and
     horizontal room it does not, so the plot area is better spent on height.
     Category charts override this from the figure's declared min_height. */
  .panel-chart { height: 240px; }
  .panel-head { padding: 8px 12px; }
  .panel-context { padding: 9px 12px 11px; }
  .panel-measures { font-size: 15px; }
  .panel-caveat { font-size: 14px; }
  .feed-title { font-size: 15px; }
  .feed-item { padding: 10px 12px; }
}
}

/* ------------------------------------------------------------- consent --- */
/* A strip along the bottom rather than a modal overlay. The dashboard stays
   fully usable with it open, because nothing on it depends on analytics —
   blocking the content to demand a decision would be pressure, not consent. */
.consent {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 60;
  background: var(--raise);
  border-top: 1px solid var(--rule-lt);
}
.consent[hidden] { display: none; }
.consent-body {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 14px 24px;
  display: flex;
  align-items: center;
  gap: 24px;
}
.consent-text {
  font-family: var(--prose);
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--text);
  margin: 0;
  flex: 1;
  max-width: 78ch;
}
.consent-actions { display: flex; gap: 10px; flex: 0 0 auto; }
.consent-btn {
  font-family: var(--mono);
  font-size: 13.5px;
  padding: 7px 16px;
  background: transparent;
  color: var(--dim);
  border: 1px solid var(--rule-lt);
  cursor: pointer;
  white-space: nowrap;
}
.consent-btn:hover { color: var(--text); border-color: var(--dim); }
/* Accept is not styled louder than Decline. Making the accept button the
   obvious one is a dark pattern, and EU guidance treats equally prominent
   choices as a condition of valid consent. */
.consent-btn.primary { color: var(--text); border-color: var(--dim); }
.consent-btn.primary:hover { color: var(--bright); border-color: var(--amber); }
.consent-btn:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }

@media (max-width: 760px) {
  .consent-body { flex-direction: column; align-items: stretch; gap: 12px; padding: 14px 16px; }
  .consent-text { font-size: 14px; }
  .consent-actions { justify-content: stretch; }
  .consent-btn { flex: 1; padding: 10px 12px; }
}


/* ------------------------------------------------------------- support --- */
/* Built from the same parts as a data panel (surface, hairline, accent rule)
   so it reads as part of the dashboard rather than an embedded widget. The
   cup is drawn in the rail's own line style instead of a third-party logo. */
.support-card {
  background: var(--panel);
  display: flex; gap: 22px; align-items: flex-start;
  padding: 24px 22px;
  border-left: 3px solid var(--accent);
}
.support-cup {
  flex: none; width: 56px; height: 56px;
  fill: none; stroke: var(--accent); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.support-body { min-width: 0; max-width: 62ch; }
.support-lead {
  margin: 2px 0 8px; font-size: 16.5px; font-weight: 600; color: var(--bright);
}
.support-text, .support-fine {
  font-family: var(--prose); color: var(--dim); margin: 0;
}
.support-text { font-size: 16px; line-height: 1.6; }
.support-fine { font-size: 14px; line-height: 1.55; margin-top: 14px; }
.support-btn {
  display: inline-flex; align-items: center; gap: 8px;
  margin-top: 18px; padding: 11px 18px;
  background: var(--accent); color: var(--void);
  border-radius: 999px;   /* same pill as the theme switch */
  font-family: var(--mono); font-size: 14px; font-weight: 600;
  text-decoration: none; overflow-wrap: anywhere;
  transition: filter 120ms ease, transform 120ms ease;
}
:root[data-theme="light"] .support-btn { color: var(--panel); }
.support-btn:hover { filter: brightness(1.08); }
.support-btn:active { transform: translateY(1px); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@media (max-width: 760px) {
  .support-card { flex-direction: column; gap: 14px; padding: 18px 16px; }
  .support-cup { width: 44px; height: 44px; }
  .support-btn { display: flex; justify-content: center; text-align: center; }
}


/* --------------------------------------------------------------- about --- */
.byline {
  margin: 14px 0 0; font-size: 13px; color: var(--dim); letter-spacing: 0.02em;
}
.byline a, .rail-disclaimer a {
  color: inherit; text-decoration: underline;
  text-decoration-color: var(--rule-lt); text-underline-offset: 3px;
}
.byline a:hover, .rail-disclaimer a:hover { color: var(--amber); }
.author-card {
  background: var(--panel); padding: 24px 22px;
  border-left: 3px solid var(--amber);
}
.author-name { margin: 0; font-size: 18px; font-weight: 600; color: var(--bright); }
.author-role {
  margin: 4px 0 14px; font-size: 12.5px; color: var(--amber); letter-spacing: 0.04em;
}
.author-bio {
  font-family: var(--prose); font-size: 16px; line-height: 1.62;
  color: var(--text); max-width: 64ch; margin: 0;
}
.author-links { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.author-link {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 15px; border: 1px solid var(--rule-lt); border-radius: 999px;
  color: var(--text); text-decoration: none; font-size: 13.5px;
  transition: border-color 120ms ease, color 120ms ease;
}
.author-link:hover { border-color: var(--amber); color: var(--amber); }
@media (max-width: 760px) {
  .author-card { padding: 18px 16px; }
}


/* Caspian gas corridor: readings under the map. Three cards on a wide screen,
   stacked on a phone. A grey card means "no reading", never zero. */
.gas-readings {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  padding: 12px 16px 4px;
}
.gas-card {
  border: 1px solid var(--rule-lt);
  border-top: 3px solid #ffa028;
  padding: 10px 12px 12px;
  min-width: 0;
}
.gas-card.none { border-top-color: var(--rule-lt); opacity: .75; }
.gas-card-name { font-size: 12px; font-weight: 600; }
.gas-card-route, .gas-card-meta { font-size: 11px; color: var(--dim, inherit); opacity: .85; margin-top: 2px; }
.gas-card-val { font-size: 26px; font-weight: 600; margin-top: 6px; line-height: 1.1; }
.gas-card-val span { font-size: 12px; font-weight: 400; opacity: .8; }
.gas-bar { height: 8px; border-radius: 4px; background: var(--rule-lt); margin: 8px 0 6px; overflow: hidden; }
.gas-bar i { display: block; height: 100%; background: var(--accent); }
.gas-spark { display: block; width: 100%; height: 24px; margin: 6px 0 2px; }
.gas-spark polyline { fill: none; stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
@media (max-width: 640px) {
  .gas-readings { grid-template-columns: 1fr; }
}
:root[data-theme="light"] .gas-card { border-top-color: #9a4508; }

/* ---------------------------------------------------- ukraine map figure --- */
.ua-figure { margin: 20px 0 0; max-width: var(--content-max); background: var(--panel);
             border: 1px solid var(--rule); }
.ua-figure img { display: block; width: 100%; height: auto; }
.ua-figure figcaption { padding: 16px 18px 18px; border-top: 1px solid var(--rule); }
.ua-figure .panel-measures, .ua-figure .panel-caveat { max-width: 78ch; }
.ua-figure a { color: inherit; text-decoration: underline; }

/* ------------------------------------------------ cyber incident snapshot --- */
.cx-head { max-width: var(--content-max); margin: 28px 0 14px; }
.cx-head h3 { font-size: 15px; font-weight: 500; margin: 0 0 8px; color: var(--text); }
.cx-head p { font-family: var(--prose); font-size: 15.5px; line-height: 1.6; color: var(--dim); max-width: 78ch; margin: 0; }
.cx-chart { background: var(--panel); margin: 0; padding: 14px 16px 16px; min-width: 0; }
.cx-chart .panel-title { margin: 0 0 4px; }
.cx-chart .panel-src { white-space: normal; margin: 0 0 12px; }
.cx-list { list-style: none; margin: 6px 0 4px; padding: 0; display: grid; gap: 7px; }
.cx-list li { display: grid; grid-template-columns: minmax(150px, 1.4fr) 2fr 2.2em; align-items: center; gap: 10px; font-size: 13px; color: var(--text); }
.cx-name small, .cx-val small { display: block; font-size: 11px; color: var(--faint); }
.cx-bar { background: var(--rule); height: 14px; position: relative; }
.cx-bar i { position: absolute; inset: 0 auto 0 0; background: var(--cyber, #9b7cc8); }
.cx-val { text-align: right; font-variant-numeric: tabular-nums; }
.cx-context { max-width: var(--content-max); margin: 20px 0; padding: 14px 18px; background: var(--panel); border: 1px solid var(--rule); }
.cx-context ul { margin: 8px 0 10px; padding-left: 18px; font-family: var(--prose); font-size: 15px; line-height: 1.6; color: var(--dim); max-width: 86ch; }
.cx-context li { margin-bottom: 6px; }
.cx-context a { color: inherit; text-decoration: underline; }
.cx-context strong { color: var(--text); font-weight: 500; }

/* ================================================== data health strip === */
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.health { border-bottom: 1px solid var(--rule-lt); background: var(--panel); --hc: var(--dim); }
.health[data-state="ok"]    { --hc: var(--defense); }
.health[data-state="stale"] { --hc: var(--amber); background: color-mix(in srgb, var(--amber) 7%, var(--panel)); }
.health-bar {
  all: unset; box-sizing: border-box; width: 100%; cursor: pointer;
  display: flex; align-items: center; gap: 10px 16px; flex-wrap: wrap;
  padding: 8px 20px; font: 12px var(--mono); color: var(--dim);
}
.health-bar:focus-visible { outline: 2px solid var(--amber); outline-offset: -2px; }
.health-bar:hover .health-more { color: var(--text); }
.health-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--hc); flex: none; }
.health-main { color: var(--text); }
.health-sub { color: var(--hc); }
.health[data-state="loading"] .health-sub { color: var(--faint); }
.health-more { margin-left: auto; color: var(--faint); }
.health-caret { display: inline-block; transition: transform 120ms ease; }
.health-bar[aria-expanded="true"] .health-caret { transform: rotate(180deg); }
.health-list { padding: 4px 20px 14px; border-top: 1px solid var(--rule); }
.health-table { width: 100%; border-collapse: collapse; font: 12px var(--mono); margin-top: 6px; }
.health-table th, .health-table td { text-align: left; padding: 5px 10px 5px 0; vertical-align: baseline; border-bottom: 1px solid var(--rule); font-weight: 400; }
.health-table thead th { color: var(--faint); font-size: 11px; }
.health-table tbody th { color: var(--text); }
.health-table td { color: var(--dim); }
.health-table .panel-flag { margin-left: 0; }
.health-abs { color: var(--faint); }
.health-table tr.health-note td { font: 12.5px var(--prose); color: var(--text); border-bottom: 1px solid var(--rule); padding: 2px 0 7px 9px; border-left: 2px solid var(--amber); }
.health-table tr.health-note.error td { border-left-color: var(--rising); }
.health-foot { font: 12.5px/1.5 var(--prose); color: var(--dim); margin: 10px 0 0; max-width: 80ch; }
.panel-flag.ok { color: var(--defense); }
@media (max-width: 760px) {
  .health-bar { padding: 8px 14px; }
  .health-list { padding: 4px 14px 12px; overflow-x: auto; }
  .health-abs { display: none; }
}

/* ===================================================== tags and legend === */
.tag {
  all: unset; box-sizing: border-box; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
  font: 11px var(--mono); letter-spacing: 0.02em; color: var(--dim);
  padding: 2px 6px; margin-left: 8px; white-space: nowrap;
  border: 1px solid var(--rule-lt);
}
span.tag { cursor: default; margin-left: 0; }
.tag:hover { color: var(--text); border-color: var(--dim); }
.tag:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
/* Each idea has its own mark as well as its own words, so none relies on colour. */
.tag-minimum::before  { content: '\2265'; }                 /* at least */
.tag-estimate::before { content: '\2248'; }                 /* approximately */
.tag-media::before {
  content: ''; width: 9px; height: 8px;
  background: repeating-linear-gradient(90deg, currentColor 0 2px, transparent 2px 4px);
}

.legend { max-width: var(--content-max); padding: 14px 20px 6px; }
.legend h2 { font: 500 13px var(--mono); color: var(--text); margin: 0 0 10px; letter-spacing: 0.02em; }
.legend-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin: 0; }
.legend-grid > div { background: var(--panel); padding: 10px 12px 12px; transition: background-color 120ms ease; }
.legend-grid > div.mark { background: var(--raise); box-shadow: inset 0 2px 0 var(--amber); }
.legend-grid dt { margin: 0 0 7px; }
.legend-grid dd { margin: 0; font: 14px/1.5 var(--prose); color: var(--dim); }
.legend-foot { font: 13px/1.5 var(--prose); color: var(--dim); margin: 8px 0 0; }
.panel-head-r { display: inline-flex; align-items: baseline; gap: 12px; flex: none; }
.panel-head { position: relative; flex-wrap: wrap; }
@media (max-width: 1020px) { .legend-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .legend { padding: 12px 14px 4px; } .legend-grid { grid-template-columns: 1fr; } }

/* ======================================================== panel download === */
.panel-dl { position: relative; font: 11.5px var(--mono); }
.panel-dl summary {
  list-style: none; cursor: pointer; color: var(--dim);
  padding: 1px 7px; border: 1px solid var(--rule-lt);
}
.panel-dl summary::-webkit-details-marker { display: none; }
.panel-dl summary:hover, .panel-dl[open] summary { color: var(--text); border-color: var(--amber); }
.panel-dl summary:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.panel-dl-menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; width: 230px;
  background: var(--raise); border: 1px solid var(--rule-lt); padding: 8px 10px 10px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.35);
}
.panel-dl-menu a {
  display: inline-block; margin-right: 8px; padding: 3px 10px; color: var(--text);
  text-decoration: none; border: 1px solid var(--rule-lt);
}
.panel-dl-menu a:hover { border-color: var(--amber); color: var(--amber); }
.panel-dl-menu a:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; }
.panel-dl-menu p { font: 12px/1.45 var(--prose); color: var(--dim); margin: 8px 0 0; }
.legend-grid .panel-flag { margin-left: 0; }
