:root {
  /* Base palette — flygplatsens egen visuella värld (skyltar, ATC-instrument,
     sektionskartor), inte generisk mörk fintech-terminal. Fast mörkt tema,
     inget ljust läge — se DESIGN_BRIEF.md. */
  --bg: #0b1220;
  --panel: #131b2c;
  --panel2: #0f1729;
  --border: #263349;
  --text: #ede6d6;
  --text-dim: #8b96ac;
  --text-faint: #5a6580;
  --accent: #f2b705;
  --accent-ink: #3b2b00;
  --high: #d4507a;
  --high-bg: #3a1a28;
  --high-ink: #f6c3d4;
  --med: #e8871e;
  --med-bg: #3a2812;
  --med-ink: #f7ce9c;
  --low: #4a5a78;
  --low-bg: #1b2436;
  --low-ink: #aeb9ce;
  --verified: #3fa34d;
  --verified-bg: #123a1e;
  --verified-ink: #a8e8bc;
  /* Extension: "new installation" category needs its own token too — the
     brief's own mockup hardcodes a teal for .cat.new, which would violate
     "never hardcode hex in components" if copied as-is. */
  --new: #6fd9b8;
  --new-bg: #0f3a30;
  /* SLT1 lifecycle badge (docs/architecture/rwi-signal-temporal-relevance-
     opportunity-lifecycle-design.md) - "realized/historical" needs its own
     distinct, calm identity, deliberately separate from every other badge
     color on the page (active/watch/stale reuse the existing
     verified/med/low tokens below, since their meaning - "good now",
     "uncertain", "quiet, needs research" - already matches those hues). */
  --historical: #b9a6e0;
  --historical-bg: #241c3a;

  --font-display: "Space Grotesk", sans-serif;
  --font-body: "IBM Plex Sans", sans-serif;
  --font-mono: "IBM Plex Mono", monospace;

  --radius: 6px;
  --radius-sm: 4px;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35);
}

* {
  box-sizing: border-box;
}

html {
  color-scheme: dark;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  /* The full-bleed hero (.hero-v2, "RWI - Juicy Design Mission #2") breaks
     out of .wrap's max-width via calc(-50vw + 50%) - the standard fix for
     that technique's own sub-pixel scrollbar-width overflow quirk. */
  overflow-x: hidden;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.wrap {
  max-width: 1080px;
  margin: 0 auto;
  padding: 0 20px 64px;
}

header.site {
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  margin-bottom: 32px;
}

header.site .wrap {
  padding-top: 20px;
  padding-bottom: 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 20px;
  justify-content: space-between;
}

.brand {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.brand a {
  color: var(--text);
}

nav.site a {
  color: var(--text-dim);
  margin-left: 18px;
  font-size: 0.92rem;
  font-weight: 500;
}

nav.site a:first-child {
  margin-left: 0;
}

/* RWI HQ "Bilingual Static Site - Polish" mission (+ "Micro-Polish"
   follow-up): the language switcher reads as its own UI group, not just
   another nav link - a fixed left margin plus a subtle divider (same
   --border token the header itself already uses), no pill/background so
   the existing dark visual language is untouched. margin-left is a fixed
   px value on .lang-switch's own box, so the gap is identical regardless
   of the preceding nav item's text width/language (Ordlista vs Glossary) -
   bumped from 24px to 40px in the Micro-Polish follow-up since 24px still
   read as crowded in manual review. */
.lang-switch {
  margin-left: 40px;
  padding-left: 20px;
  border-left: 1px solid var(--border);
}

nav.site a:hover {
  color: var(--text);
  text-decoration: none;
}

h1 {
  font-family: var(--font-display);
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  margin: 0 0 6px;
}

h2 {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 12px;
}

.subtitle {
  color: var(--text-dim);
  margin: 0 0 28px;
  font-size: 0.98rem;
}

/* Statusremsa (toppbar) — kompakt, max 4-5 tal, de viktigaste. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0;
  margin-bottom: 32px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.stat {
  padding: 14px 20px;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.stat:nth-last-child(-n + 1) {
  border-right: none;
}

@media (min-width: 561px) {
  .stat {
    border-bottom: none;
  }
}

.stat .label {
  color: var(--text-dim);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.stat .value {
  font-family: var(--font-mono);
  font-size: 1.7rem;
  font-weight: 500;
  color: var(--accent);
  margin-top: 2px;
}

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 24px;
  overflow: hidden;
}

.card .card-header {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--panel2);
  font-weight: 600;
  font-size: 0.95rem;
}

.card .card-body {
  padding: 18px;
}

/* Personal annotations (scripts/annotate_signal.py) - dashed border marks
   this as an outside note, not something the source itself said. */
/* /ordlista.html: highlight the term a #anchor link landed on. */
.card:target {
  border-color: var(--accent);
}

.card.annotation {
  border-style: dashed;
  border-color: var(--accent);
}

.card.annotation .card-header {
  background: transparent;
  border-bottom-style: dashed;
  border-bottom-color: var(--accent);
}

.card.annotation .eyebrow {
  display: block;
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
  margin-top: 2px;
}

.year-estimate {
  font-family: var(--font-mono);
  color: var(--accent);
  font-weight: 500;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}

th, td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

th {
  color: var(--text-faint);
  font-weight: 500;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--panel2);
}

tbody tr:last-child td {
  border-bottom: none;
}

tbody tr:hover {
  background: rgba(255, 255, 255, 0.02);
}

td.title {
  white-space: normal;
  min-width: 220px;
}

/* ("RWI - Mission #7J" mission) Varför nu? - a single full-width, muted
   subtitle line directly under the Signal row it explains, never a badge
   or chip (Mission #7I's own "no clutter" finding). Reuses .timeline-meta's
   own color/size, tightened to read as attached to the row above; the
   shared td border-bottom (kept, not overridden) is what visually closes
   the pair before the next Signal's own row starts. */
tr.attention-row td.attention-reason {
  white-space: normal;
  padding-top: 0;
  padding-bottom: 10px;
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Flygplats-skylt — signaturkomponenten. Överallt en flygplatskod visas. */
.sign {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 12px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  letter-spacing: 0.02em;
  cursor: default;
}

/* Hover affordance when the sign badge is a link's payload (airport-code
   cells in list/table rows) - airport_detail.html's own badge in the page
   <h1> isn't wrapped in <a>, so it's unaffected and keeps cursor:default.
   Rows with no code render the plain "–" fallback span instead (see
   _components.html's sign() macro), which never carries .sign, so it's
   untouched by these rules too. */
a .sign {
  cursor: pointer;
}

a:hover .sign {
  filter: brightness(1.15);
}

/* Kategori-badge — text kommer alltid från den centrala mappningen, aldrig
   ett råt databasvärde. */
.cat {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  display: inline-block;
  white-space: nowrap;
}

.cat.new { background: var(--new-bg); color: var(--new); }
.cat.replace { background: var(--med-bg); color: var(--med-ink); }
.cat.incident { background: var(--high-bg); color: var(--high-ink); }
.cat.study { background: var(--low-bg); color: var(--low-ink); }

/* SLT1 lifecycle badge — a separate, additional badge from .cat/.pill,
   never replacing either. Text always comes from
   app/static_export/presentation.py's LIFECYCLE_PRESENTATION, never a raw
   enum value. */
.lifecycle {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  display: inline-block;
  white-space: nowrap;
  border: 1px solid transparent;
}

.lifecycle.active { background: var(--verified-bg); color: var(--verified-ink); border-color: var(--verified); }
.lifecycle.watch { background: var(--med-bg); color: var(--med-ink); }
.lifecycle.historical { background: var(--historical-bg); color: var(--historical); }
.lifecycle.stale { background: var(--low-bg); color: var(--low-ink); }
.lifecycle.unknown { background: var(--panel2); color: var(--text-dim); }

/* Compact per-state count legend above the signals table. */
.lifecycle-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 10px 0 16px;
}

.lifecycle-stat {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--panel2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 4px 10px;
}

.lifecycle-stat-count {
  font-family: var(--font-mono);
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Confidence som gauge, inte badge-text. */
.gauge {
  display: flex;
  gap: 2px;
  align-items: center;
}

.gauge span:not(.gauge-label) {
  width: 5px;
  height: 12px;
  border-radius: 1px;
  background: var(--border);
}

.gauge.high span:nth-child(-n + 3) { background: var(--high); }
.gauge.med span:nth-child(-n + 2) { background: var(--med); }
.gauge.low span:nth-child(-n + 1) { background: var(--low-ink); }

.gauge-label {
  font-size: 10px;
  color: var(--text-faint);
  margin-left: 6px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.pill.status {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 500;
  white-space: nowrap;
  background: var(--panel2);
  border: 1px solid var(--border);
  color: var(--text-dim);
}

/* .pill.status doubles as a source-type badge linking to /ordlista.html
   (see _components.html's source_badge macro) - give the linked variant a
   visible hover affordance distinct from the plain (non-clickable) span
   version, without changing its resting look. */
a.pill.status {
  cursor: help;
}

a.pill.status:hover {
  border-color: var(--accent);
  color: var(--text);
  text-decoration: none;
}

/* Inline glossary link - used for the confidence gauge label and the
   "Bekräftad leverantör" pill's prefix, where a full badge would look out
   of place mid-text. color:inherit so it matches whatever muted/accent ink
   it's embedded in; the dotted underline is the only "this is clickable"
   signal until hover. */
.gloss-link {
  color: inherit;
  cursor: help;
  border-bottom: 1px dotted currentColor;
  text-decoration: none;
}

.gloss-link:hover {
  border-bottom-style: solid;
  text-decoration: none;
}

/* Explicit "we checked and can't confirm this" - dashed/muted so it never
   reads like a normal status pill (e.g. "Bana 24") stating a known fact. */
.pill.unconfirmed {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 500;
  white-space: nowrap;
  background: transparent;
  border: 1px dashed var(--text-faint);
  color: var(--text-faint);
}

/* A signal graduated to a real Installation (scripts/graduate_signal_to_installation.py) -
   same "confirmed fact" green as .pill.vendor, so "done" reads distinctly
   from a still-open status instead of like a broken/unrecognized value. */
.pill.done {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  background: var(--verified-bg);
  border: 1px solid var(--verified);
  color: var(--verified-ink);
}

.pill.construction, .pill.procurement, .pill.design, .pill.funded, .pill.identified, .pill.planning, .pill.review, .pill.unknown {
  font-size: 0.78rem;
}
.pill.construction { border-color: var(--med); color: var(--med-ink); }
.pill.funded { border-color: var(--verified); color: var(--verified); }
.pill.unknown { color: var(--text-dim); }
.project-type { color: var(--text-dim); font-size: 0.88rem; }
.score-secondary { font-size: 1rem; color: var(--text-faint); opacity: .8; }

/* Confirmed vendor - a fact the source explicitly named, not a guess -
   distinct green so it never reads like the category/confidence pills. */
.pill.vendor {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  white-space: nowrap;
  background: var(--verified-bg);
  border: 1px solid var(--verified);
  color: var(--verified-ink);
}

.score {
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--text);
}

.mono {
  font-family: var(--font-mono);
}

.src {
  font-size: 0.78rem;
  color: var(--text-faint);
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 18px;
}

.filters input, .filters select {
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel2);
  color: var(--text);
  font-size: 0.9rem;
  font-family: inherit;
}

.filters input {
  flex: 1 1 220px;
}

.filter-checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-dim);
  font-size: 0.9rem;
  white-space: nowrap;
  cursor: pointer;
}

/* "Bevaka" (watch) star - client-side only, see static/watch.js. Fixed
   rem size so it reads the same whether it's sitting in a table cell or
   next to an <h1> on signal_detail.html. */
.star-toggle {
  background: none;
  border: none;
  padding: 0;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  color: var(--text-faint);
  vertical-align: middle;
}

.star-toggle:hover {
  color: var(--accent);
}

.star-toggle.watched {
  color: var(--accent);
}

/* Plain (non-link) hover tooltip via the native title attribute - same
   dotted "help" affordance as .gloss-link, but without implying a
   clickable destination (no glossary entry backs this one). */
.info-hint {
  color: var(--text-faint);
  cursor: help;
  border-bottom: 1px dotted currentColor;
  font-size: 0.8em;
}

.empty-state {
  padding: 40px 18px;
  text-align: center;
  color: var(--text-dim);
}

dl.meta {
  display: grid;
  grid-template-columns: 160px 1fr;
  gap: 10px 16px;
  margin: 0;
}

dl.meta dt {
  color: var(--text-dim);
  font-size: 0.85rem;
}

dl.meta dd {
  margin: 0;
}

.breadcrumbs {
  color: var(--text-dim);
  font-size: 0.88rem;
  margin-bottom: 8px;
}

/* "← Tillbaka" (history.back()) next to the "← Flygplatser" breadcrumb link -
   a <button>, not an <a>, since it has no real href (see airport_detail.html) -
   styled to read as a plain inline link like its neighbor. */
.link-button {
  background: none;
  border: none;
  padding: 0;
  margin-left: 14px;
  color: var(--accent);
  font: inherit;
  font-size: inherit;
  cursor: pointer;
}

.link-button:hover {
  text-decoration: underline;
}

.grid-2 {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 24px;
  align-items: start;
}

@media (max-width: 760px) {
  .grid-2 {
    grid-template-columns: 1fr;
  }
  table, thead, tbody, th, td, tr {
    display: block;
  }
  thead {
    display: none;
  }
  tbody tr {
    border-bottom: 1px solid var(--border);
    padding: 10px 14px;
  }
  td {
    border-bottom: none;
    white-space: normal;
    padding: 4px 0;
  }
  td::before {
    content: attr(data-label);
    display: block;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-faint);
    margin-bottom: 2px;
  }
}

@media (max-width: 560px) {
  .stat-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .stat:nth-child(2n) {
    border-right: none;
  }
}

.money {
  font-variant-numeric: tabular-nums;
  font-family: var(--font-mono);
}

/* airport_detail.html's "Tidslinje" — a chronological complement to the
   separate Installations/Signals/Incidents tables, not a replacement.
   Reuses the same .cat category coloring (new/replace/incident/study) as
   the Signals table so a category means the same thing everywhere. */
.timeline-item {
  display: grid;
  grid-template-columns: 56px 20px 1fr;
  column-gap: 12px;
  position: relative;
}

.timeline-item.timeline-item-undated {
  grid-template-columns: 20px 1fr;
}

.timeline-item:not(:last-child) {
  padding-bottom: 16px;
}

.timeline-year {
  text-align: right;
  color: var(--text-dim);
  font-size: 0.85rem;
  padding-top: 2px;
}

.timeline-rail {
  position: relative;
  display: flex;
  justify-content: center;
}

.timeline-item:not(:last-child) .timeline-rail::before {
  content: "";
  position: absolute;
  top: 16px;
  bottom: -16px;
  width: 2px;
  background: var(--border);
}

.timeline-undated .timeline-rail::before {
  content: none;
}

.timeline-dot {
  display: block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  margin-top: 3px;
  position: relative;
  z-index: 1;
  border: 2px solid var(--panel);
}

.timeline-dot.new { background: var(--new); }
.timeline-dot.replace { background: var(--med); }
.timeline-dot.incident { background: var(--high); }
.timeline-dot.study { background: var(--low-ink); }

.timeline-title {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.timeline-title a {
  color: var(--text);
  font-weight: 500;
}

.timeline-title a:hover {
  color: var(--accent);
}

.timeline-meta {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin-top: 2px;
}

/* ("RWI - Juicy Design Mission #4 - Visual Polish Checkpoint" mission)
   Keeps "event type · context" and the SLT1 lifecycle badge on one line,
   badge pushed to the trailing edge - the lifecycle read stays visible
   without claiming a full row of its own next to the real event headline
   (the mission's own "lifecycle remains useful but should not overpower
   the actual event"). */
.timeline-meta-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px 10px;
  flex-wrap: wrap;
}

.timeline-meta-row .timeline-meta { margin-top: 0; }

/* Financial caveats/source citations stay visually secondary (smaller,
   muted, never boxed) but always visible - never hidden behind a toggle. */
.timeline-caveat { margin-top: 6px; }
.timeline-source { margin-top: 4px; }

@media (max-width: 480px) {
  .timeline-item {
    grid-template-columns: 40px 16px 1fr;
    column-gap: 8px;
  }
  .timeline-year {
    font-size: 0.76rem;
  }
  .timeline-meta-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
}

/* Landing spot for a Tidslinje click into the Installationer/Incidenter
   tables below - .card:target (above) already covers /ordlista.html
   anchors, but these two targets aren't .card elements. */
[id^="installation-"]:target {
  background: rgba(242, 183, 5, 0.1);
  border-radius: var(--radius-sm);
}

tbody tr:target td {
  background: rgba(242, 183, 5, 0.1);
}

/* index.html's install-trend chart - pure SVG, no chart library. Two
   stacked panels (per-year bars, cumulative line) share the same x mapping
   (see build.py's _trend_view) so their gridlines line up vertically. */
.trend-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin-bottom: 12px;
  font-size: 0.82rem;
  color: var(--text-dim);
}

.trend-legend-item {
  display: inline-flex;
  align-items: center;
}

.trend-swatch {
  display: inline-block;
  width: 11px;
  height: 11px;
  border-radius: 2px;
  margin-right: 6px;
}

.trend-swatch-install {
  background: var(--accent);
}

.trend-swatch-signal {
  background-image: repeating-linear-gradient(45deg, var(--low-ink), var(--low-ink) 1.5px, transparent 1.5px, transparent 4px);
  border: 1px dashed var(--low-ink);
}

/* SVG text scales with the viewBox like everything else in it - on a
   narrow phone that shrinks the 9-unit tick labels into illegible dust.
   Rather than hide labels to compensate, give the chart a min-width and
   let it scroll horizontally (same idea as the table->card breakpoint
   elsewhere, applied to a chart instead: adapt, don't render illegibly). */
.trend-scroll {
  overflow-x: auto;
}

.trend-chart {
  display: block;
  width: 100%;
  min-width: 700px;
  height: auto;
}

.trend-chart-cumulative {
  margin-top: 2px;
}

.trend-gridline {
  stroke: var(--border);
  stroke-width: 1;
}

.trend-baseline {
  stroke: var(--border);
  stroke-width: 1;
}

.trend-bar-install {
  fill: var(--accent);
}

.trend-bar-install:hover {
  opacity: 0.82;
}

/* Forecast/planned - deliberately muted + hatched + dashed, never solid
   gold, so it never reads as confirmed fact next to the installation bars. */
.trend-bar-signal {
  fill: url(#trend-hatch);
  stroke: var(--low-ink);
  stroke-width: 1;
  stroke-dasharray: 2 1;
}

.trend-bar-signal:hover {
  opacity: 0.82;
}

.trend-area {
  fill: var(--accent);
  opacity: 0.12;
  stroke: none;
}

.trend-line {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
}

.trend-point {
  fill: var(--bg);
  stroke: var(--accent);
  stroke-width: 1.5;
}

.trend-point:hover {
  fill: var(--accent);
}

.trend-tick-label {
  fill: var(--text-faint);
  font-family: var(--font-mono);
  font-size: 9px;
}

.trend-caption {
  color: var(--text-faint);
  font-size: 0.8rem;
  margin: 10px 0 0;
}

footer.site {
  color: var(--text-faint);
  font-size: 0.85rem;
  margin-top: 40px;
}

/* Slice 3: calm overview-first intelligence surfaces. */
.hero-intelligence { margin:10px 0 30px; }
.hero-intelligence h1 { font-size:clamp(2.2rem,5vw,4.5rem); max-width:760px; margin:6px 0 12px; letter-spacing:-.045em; }
.hero-copy { padding:18px 0; }
.card-header { display:flex; justify-content:space-between; align-items:center; gap:12px; }
.card-action { font-size:.82rem; font-weight:400; }
.intelligence-feed { padding:4px 18px 12px; }
.intelligence-head,.intelligence-item { display:grid; grid-template-columns:minmax(120px,.45fr) minmax(180px,.7fr) minmax(0,1fr) minmax(88px,max-content); column-gap:24px; }
.intelligence-head { color:var(--text-faint); font-size:.7rem; letter-spacing:.08em; padding:10px 0; border-bottom:1px solid var(--border); }
.intelligence-item { align-items:center; padding:14px 0; border-bottom:1px solid var(--border); }
.intelligence-item:last-child { border-bottom:0; }
.intelligence-type { display:flex; align-items:center; }
.intelligence-place { display:grid; gap:3px; font-size:.85rem; color:var(--text-dim); }
.intelligence-place a,.intelligence-story a { color:var(--text); font-weight:600; }
.intelligence-date { color:var(--text-faint); font-size:.78rem; text-align:right; white-space:nowrap; }
.secondary-disclosure { margin:14px 16px; border-top:1px solid var(--border); }
.secondary-disclosure summary { cursor:pointer; color:var(--text-dim); padding:13px 0; }
.history-disclosure { margin-top:18px; }
.history-disclosure > summary { list-style:none; }
.history-disclosure > summary::-webkit-details-marker { display:none; }
.history-disclosure > summary::after, .source-detail > summary::after { content:'▸'; margin-left:auto; color:var(--accent); }
.history-disclosure[open] > summary::after, .source-detail[open] > summary::after { content:'▾'; }
.source-detail { border-top:1px solid var(--border); padding-top:10px; }
.source-detail summary { cursor:pointer; display:flex; align-items:center; color:var(--text-dim); font-size:.88rem; }
summary:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:3px; }
.disclosure-body { padding:0 0 4px; color:var(--text-dim); }
.disclosure-body p { margin:0 0 8px; }

/* ("RWI - Juicy Design Mission #4 - Visual Polish Checkpoint" mission)
   "Underlag" - claim-rich sources (their own SOURCE block + numbered
   evidence findings) read as an analyst dossier; provenance-only sources
   (no governed claim) render as compact, thin rows - never the same
   visual weight as a real transcribed claim. Gold (var(--accent)) marks
   the finding label only, not a filled block. */
.evidence-eyebrow {
  color: var(--text-faint);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.evidence-source-title { font-weight: 500; margin-top: 2px; }
.evidence-source-meta { color: var(--text-dim); font-size: 0.85rem; margin: 2px 0 14px; }

.evidence-findings { display: flex; flex-direction: column; gap: 16px; }
.evidence-finding-label {
  color: var(--accent);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.evidence-finding-statement { margin: 6px 0 4px; font-size: 1rem; }

/* The original-language excerpt reads as quoted source text, not RWI's
   own commentary - a left rule + italic, distinct from the claim
   statement's own plain prose directly above it. */
.evidence-excerpt .card-body {
  border-left: 2px solid var(--border);
  padding-left: 12px;
  font-style: italic;
  color: var(--text-dim);
}

.evidence-provenance-note {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin: 4px 0 12px;
}
.evidence-provenance-list { display: flex; flex-direction: column; }
.evidence-provenance-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 2px 16px;
  padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.evidence-provenance-row:last-child { border-bottom: none; }
.evidence-provenance-title { font-weight: 500; }
.evidence-provenance-meta { color: var(--text-dim); font-size: 0.82rem; }

@media (max-width: 480px) {
  .evidence-provenance-row { flex-direction: column; align-items: flex-start; gap: 2px; }
}
@media (max-width:700px) { .intelligence-head { display:none; } .intelligence-item { grid-template-columns:minmax(0,1fr) auto; gap:6px 12px; padding:16px 0; } .intelligence-place { grid-column:1; grid-row:1; } .intelligence-type { grid-column:2; grid-row:1; justify-self:end; } .intelligence-story { grid-column:1 / -1; grid-row:2; } .intelligence-date { grid-column:1 / -1; grid-row:3; text-align:left; margin-top:1px; } }

/* Visual redesign: editorial aviation intelligence, not an admin panel. */
body { background:linear-gradient(180deg, #0d1421 0, var(--bg) 32rem); }
header.site { background:transparent; border-color:rgba(139,150,172,.26); }
/* "RWI - Juicy Design Mission #2 - Visual Correction Pass" mission: a
   substantially wider desktop canvas (V2 reference's own strongest single
   lesson - "intelligence workstation", not a narrow report column). */
.wrap { max-width:1440px; }
h1 { font-size:clamp(1.9rem,3vw,2.8rem); letter-spacing:-.04em; }
.hero-intelligence { margin:8px 0 26px; }
.hero-intelligence h1 { font-size:2.625rem; line-height:.94; letter-spacing:-.06em; max-width:none; white-space:nowrap; }
.hero-copy { padding-bottom:10px; }
.hero-copy .subtitle { font-size:1.1rem; max-width:530px; margin-top:24px; margin-bottom:14px; }
.stat-grid { background:transparent; border:0; border-radius:0; margin:0 0 42px; overflow:visible; gap:24px; }
.stat { padding:0; border:0!important; }.stat .value { font-size:1.8125rem; }
.card { background:transparent; border:0; border-radius:0; box-shadow:none; margin-bottom:38px; overflow:visible; }
.card .card-header { padding:0 0 12px; border-bottom:1px solid rgba(139,150,172,.3); background:transparent; font-size:1.1rem; }
/* Dashboard-only: .stat-grid appears solely on index.html, so this never touches airport/Signals/Signal-detail card headings. */
.stat-grid ~ .card .card-header { font-size:1rem; }
.card .card-body { padding:18px 0; }
.intelligence-feed { padding:0; }.intelligence-head { border-color:rgba(139,150,172,.3); }.intelligence-item { padding:17px 0; border-color:rgba(139,150,172,.26); }
.intelligence-story a { font-size:1.02rem; }.intelligence-place a { color:var(--accent); }
.trend-scroll { padding:10px 0; }.trend-caption { max-width:720px; }
table { font-size:.9rem; } th,td { padding:13px 8px; } th:first-child,td:first-child { padding-left:0; } th:last-child,td:last-child { padding-right:0; }
.grid-2 { grid-template-columns:minmax(0,1fr) 250px; gap:38px; }
.grid-2 > div:last-child .card { border-left:1px solid rgba(139,150,172,.3); padding-left:22px; }
.grid-2 > div:last-child .card .card-header { border:0; }
.history-disclosure { border-top:1px solid rgba(139,150,172,.3); padding-top:16px; }
.secondary-disclosure { margin-left:0; margin-right:0; }
.source-detail { background:rgba(19,27,44,.46); padding:12px; }
.filters { background:rgba(19,27,44,.45); border:1px solid rgba(139,150,172,.25); border-radius:0; padding:10px; }
#signals-table .title a { font-family:var(--font-display); font-weight:600; color:var(--text); }
#signals-table td:first-child + td a .sign { background:transparent; color:var(--accent); padding:0; }
@media (max-width:700px) { .hero-intelligence { margin-bottom:30px; } .hero-intelligence h1 { font-size:2.625rem; white-space:normal; } .grid-2 { grid-template-columns:1fr; gap:0; } .grid-2 > div:last-child .card { border-left:0; padding-left:0; } .stat-grid { grid-template-columns:repeat(2,1fr); gap:24px 12px; } }

/* ---------------------------------------------------------------------
   "RWI - Juicy Design Mission #1" mission: Overview V2 + BOS Airport
   Detail V2. Premium aviation-intelligence surfaces built entirely from
   the existing dark-navy/gold token system above - no new palette. */

/* Image-less hero treatment: a faint runway-centerline motif (repeating
   dashed line, the universal aviation visual) plus a soft gold glow behind
   the headline, both pure CSS gradients - degrades to the plain hero
   (already fully readable) if this rule is ever removed, and a background
   photo can later be layered in behind these same gradients without any
   markup change. */
.hero-runway {
  position: relative;
  padding: 8px 0 4px;
  background:
    linear-gradient(90deg, transparent 0%, transparent 42%, rgba(242,183,5,.55) 50%, transparent 58%, transparent 100%) 0 78% / 100% 2px no-repeat,
    repeating-linear-gradient(90deg, rgba(237,230,214,.16) 0 22px, transparent 22px 46px) 0 78% / 100% 2px no-repeat,
    radial-gradient(ellipse 620px 260px at 18% 20%, rgba(242,183,5,.14), transparent 70%);
}

.hero-runway::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(242,183,5,.05), transparent 55%);
}

/* Marknader (Overview) - a real per-country bar list, deliberately not a
   map (no reliable/complete airport coordinate coverage exists yet - see
   build.py's _market_summary_view docstring). Bar width is share_pct,
   relative to the largest market's own count, never a percentage of a
   global total (that would misleadingly imply completeness). */
.market-summary { display: flex; flex-direction: column; gap: 10px; }
.market-row { display: grid; grid-template-columns: 140px 1fr 44px; align-items: center; gap: 14px; }
.market-country { font-size: .92rem; color: var(--text); }
.market-bar-track { height: 8px; border-radius: 4px; background: var(--panel2); overflow: hidden; }
.market-bar-fill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--new)); border-radius: 4px; min-width: 3px; }
.market-count { text-align: right; color: var(--text-dim); font-size: .88rem; }
@media (max-width: 480px) { .market-row { grid-template-columns: 92px 1fr 32px; gap: 10px; } }

/* Airport Detail "project summary" hero - one dominant status descriptor,
   category/confidence/year as plain, equally-sized secondary metadata, and
   the lifecycle badge deliberately smaller/muted so it reads as a
   qualifier, never a competing headline (see airport_detail.html's own
   comment for the full reasoning). */
/* Higher-specificity two-class selector (.card.project-summary), not
   .project-summary alone: this element is now also .card.panel-strong
   ("RWI - Juicy Design Mission #2 - Visual Correction Pass" - moved into
   the new 3-column .airport-body-grid), whose own `border` shorthand would
   otherwise cancel this border-left accent regardless of source order. */
.card.project-summary { border-left: 3px solid var(--accent); }
.project-summary .eyebrow { margin: 0 0 10px; }
.project-summary-head { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 10px; }
.project-summary-title { margin: 0 0 8px; font-size: 1.3rem; }
.project-summary-title a { color: var(--text); }
.project-summary-title a:hover { color: var(--accent); }
.project-summary-lifecycle { font-size: 10px; opacity: .88; }
.project-summary-meta { display: flex; flex-wrap: wrap; gap: 22px; margin: 14px 0 4px; }
.project-summary-meta-item { display: flex; flex-direction: column; gap: 4px; }
.project-summary-meta-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint); }
.project-summary-why { margin: 14px 0 0; padding-top: 14px; border-top: 1px solid var(--border); color: var(--text-dim); font-size: .9rem; max-width: 640px; }
.project-summary-why strong { color: var(--text); }

/* Dominant status badge - one explicit, restrained color per pipeline
   maturity tier, extending the same role vocabulary status_view() already
   assigns (.pill.construction/.funded's own existing color choices, made
   consistent across every role instead of leaving most unstyled). */
.hero-status {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -.01em;
  padding: 7px 16px;
  border-radius: var(--radius-sm);
  background: var(--panel2);
  border: 1px solid var(--border);
  color: var(--text);
}
.hero-status.completed, .hero-status.funded { background: var(--verified-bg); border-color: var(--verified); color: var(--verified-ink); }
.hero-status.construction, .hero-status.procurement { background: var(--med-bg); border-color: var(--med); color: var(--med-ink); }
.hero-status.design, .hero-status.review, .hero-status.planning { background: var(--low-bg); border-color: var(--low); color: var(--low-ink); }
.hero-status.identified, .hero-status.unknown { background: var(--panel2); border-color: var(--border); color: var(--text-dim); }

@media (max-width: 560px) {
  .project-summary { padding: 18px; }
  .project-summary-meta { gap: 16px; }
}

/* ---------------------------------------------------------------------
   "RWI - Juicy Design Mission #2" mission (initial pass) + "Visual
   Correction Pass - V2 Target" (this revision, superseding the initial
   pass's own composition wholesale): a substantially wider, denser,
   higher-contrast intelligence-workstation Overview + BOS header.
   Reference: docs/design/rwi-visual-target-v2.png - composition/hierarchy/
   density/contrast only, never a data specification (see build.py's own
   module-level recon for what was deliberately NOT copied from it: no
   world map, no invented deltas, no fake watchlist, no fabricated
   passenger/completion/financial figures, no copied percentages except
   where the exact same real, deterministic proportions were independently
   computed here). */

/* Chrome: a slightly more premium header, still 100% the same real nav
   destinations (no fake search/notifications/avatar - explicit "no fake
   features" boundary, both passes). */
header.site { background: rgba(11,18,32,.78); backdrop-filter: blur(6px); position: sticky; top: 0; z-index: 20; }
nav.site a { position: relative; padding-bottom: 2px; }
nav.site a[href$="index.html"]:first-child { color: var(--text); }

/* Cinematic hero: full-bleed image kept clearly visible (a much lighter,
   mostly-left-side overlay instead of the initial pass's near-opaque
   scrim across the whole image - V2's own explicit "image remains clearly
   visible" / "do not make the entire page pale yellow" correction), a
   taller viewport-scene min-height, and individually-bordered gold KPI
   cards (not one joined translucent panel) sitting over the hero's own
   lower region. */
.hero-v2 {
  position: relative;
  margin: -32px calc(-50vw + 50%) 44px;
  min-height: 640px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: linear-gradient(180deg, #0d1421, var(--bg));
}
.hero-v2-image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 62% 55%;
  filter: saturate(1.08) contrast(1.03);
}
.hero-v2-overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(9,14,25,.18) 0%, rgba(9,14,25,.3) 55%, var(--bg) 97%),
    linear-gradient(100deg, rgba(9,14,25,.96) 0%, rgba(9,14,25,.82) 32%, rgba(9,14,25,.28) 58%, rgba(9,14,25,.05) 78%);
}
.hero-v2-inner {
  position: relative;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
  padding: 108px 32px 0;
}
.hero-v2-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.3rem, 4.6vw, 3.75rem);
  line-height: 1.06;
  letter-spacing: -.03em;
  margin: 10px 0 18px;
  max-width: 660px;
  color: #fff;
  text-shadow: 0 3px 28px rgba(0,0,0,.55);
}
.hero-v2-accent { color: var(--accent); text-shadow: 0 0 32px rgba(242,183,5,.4); }
.hero-v2-subtitle {
  color: #fff;
  opacity: .88;
  font-size: 1.1rem;
  max-width: 480px;
  margin: 0 0 26px;
  text-shadow: 0 2px 16px rgba(0,0,0,.5);
}
.btn-primary {
  display: inline-block;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  font-size: .95rem;
  padding: 12px 24px;
  border-radius: var(--radius-sm);
  text-decoration: none;
  box-shadow: 0 4px 20px rgba(242,183,5,.28);
}
.btn-primary:hover { filter: brightness(1.08); text-decoration: none; }

/* Individually-bordered "intelligence card" KPIs - gold hairline border,
   icon-led, solid dark card (not translucent-joined) - V2's own visual
   spec for this component. Icons are plain Unicode glyphs (no icon font/
   library - Mission #1's own "lightweight existing/local techniques"
   boundary, unchanged). */
.hero-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-top: 44px;
  max-width: 900px;
}
.hero-kpi {
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgba(15,23,41,.86);
  border: 1px solid rgba(242,183,5,.32);
  border-radius: var(--radius);
  padding: 16px 16px;
  backdrop-filter: blur(4px);
}
.hero-kpi-icon {
  flex: none;
  font-size: 1.3rem;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(242,183,5,.14);
  border-radius: var(--radius-sm);
}
.hero-kpi-value {
  font-family: var(--font-mono);
  font-size: 1.7rem;
  font-weight: 500;
  color: #fff;
  line-height: 1.1;
}
.hero-kpi-label {
  color: rgba(237,230,214,.7);
  font-size: .68rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-top: 1px;
}

@media (max-width: 760px) {
  .hero-v2 { min-height: 0; margin-bottom: 32px; }
  .hero-v2-inner { padding: 84px 20px 0; }
  .hero-kpi-grid { grid-template-columns: repeat(2, 1fr); margin-top: 28px; max-width: none; }
}

/* Dashboard row 1: Signalöversikt (primary, wide) + Marknadsläge/Projekt
   per stadium - V2's own top-dashboard composition. Dashboard row 2
   (.bottom-grid, defined further below): EMAS-aktivitet över tid + Senast
   uppdaterat, side by side instead of full-width stacked. Both rows sit
   inside the same overall .wrap (now 1440px, see above), so the *desktop
   canvas itself* is wider, not merely its internal ratio.
   "V2.2 Dashboard Balance Fix": the side region was a single 380px column
   with Marknadsläge/Projekt per stadium STACKED - their combined height
   ran noticeably past Signalöversikt's own (real, content-driven) height,
   leaving visible dead space under Signalöversikt. Widened to 560px and
   made a real two-up sub-grid (both cards side by side, sharing one row
   height driven by whichever is taller) instead of a tall single-file
   stack - the exact fix that removes the imbalance without touching
   Signalöversikt's own row count/content or either side card's real data. */
.dashboard-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 560px;
  gap: 22px;
  align-items: start;
}
.dashboard-col-side { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: start; }
.panel-strong {
  background: rgba(19,27,44,.6);
  border: 1px solid rgba(139,150,172,.18);
  border-radius: var(--radius);
  padding: 4px 18px 18px;
}
.panel-strong .card-header { padding-top: 14px; }

.bottom-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 22px;
  align-items: start;
  margin-top: 0;
}
.bottom-grid > .panel-strong { margin-bottom: 0; }

@media (max-width: 1080px) {
  /* "V2.3" mission fix: bare `1fr` (i.e. the implicit `minmax(auto, 1fr)`)
     let a child's own intrinsic min-content width - specifically
     .trend-chart's `min-width: 700px` inside "EMAS-aktivitet över tid" -
     push this single-column track (and the whole page) wider than the
     real viewport at mobile widths, even though .trend-scroll's own
     `overflow-x: auto` was meant to contain exactly that. `minmax(0, 1fr)`
     (already used at desktop widths above) removes that auto-sizing
     floor, matching the fix already applied to .grid-2/.dashboard-grid's
     own desktop rule. Verified: document.scrollWidth == clientWidth at a
     real 390px viewport after this change (was 758 vs 390 before). */
  .dashboard-grid { grid-template-columns: minmax(0, 1fr); }
  .dashboard-col-side { grid-template-columns: minmax(0, 1fr); }
  .bottom-grid { grid-template-columns: minmax(0, 1fr); }
}

/* Market pulse: one dominant market card showing its REAL share of the
   real total (pct_of_total - build.py's own already-computed field, never
   a value copied from the reference image), plus a compact bar list for
   every market at the same real proportion. Deliberately not equal-weight
   spotlight cards - see build.py's _market_summary_view docstring for why
   a 1-Signal market must never look comparable to a 64-Signal one. */
.market-dominant {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 4px 0 16px;
  margin-bottom: 14px;
}
.market-dominant-flag {
  flex: none;
  font-size: 1.9rem;
  width: 52px; height: 52px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(242,183,5,.12);
  border: 1px solid rgba(242,183,5,.3);
  border-radius: var(--radius);
}
.market-dominant-country { font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; }
.market-dominant-count { color: var(--text-dim); font-size: .86rem; margin-top: 2px; }
.market-dominant-count strong { font-family: var(--font-mono); color: var(--accent); font-weight: 600; }
.market-secondary-list { display: flex; flex-direction: column; gap: 10px; }
.market-secondary-row { display: grid; grid-template-columns: 84px 1fr 34px; align-items: center; gap: 10px; font-size: .86rem; }
.market-secondary-row .market-secondary-name { color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.market-secondary-bar-track { height: 6px; border-radius: 3px; background: var(--panel2); overflow: hidden; }
.market-secondary-bar-fill { height: 100%; border-radius: 3px; background: var(--accent); opacity: .75; }
.market-secondary-row .mono { text-align: right; color: var(--text); font-size: .8rem; }

/* Stage distribution donut - pure SVG, no chart library (build.py's
   _stage_distribution_view). Horizontal composition (donut left, legend
   right) instead of the initial pass's stacked-vertical layout - tighter,
   less empty space, matches V2. Ring colors reuse the exact same
   status-role vocabulary/tokens .hero-status already established (Mission
   #1) so a color means the same maturity tier everywhere on the site. */
.stage-donut-wrap { display: flex; align-items: center; gap: 14px; }
.stage-donut { flex: none; width: 108px; height: 108px; }
.stage-donut-track { fill: none; stroke: var(--panel2); stroke-width: 13; }
.stage-donut-seg { fill: none; stroke-width: 13; stroke-linecap: butt; }
.stage-donut-total { font-family: var(--font-mono); font-size: 20px; fill: var(--text); font-weight: 500; }
.stage-donut-total-label { font-family: var(--font-body); font-size: 8px; fill: var(--text-faint); text-transform: uppercase; letter-spacing: .05em; }
.stage-donut-legend { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
/* "V2.2 Dashboard Balance Fix": no white-space:nowrap here anymore - once
   this card sits in a narrower two-up column (see .dashboard-col-side
   above), the longest real label ("Bevakas – forskningskandidat") must be
   able to wrap onto a second line instead of forcing the row (and the
   whole card) wider than its grid column. */
.stage-legend-row { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 7px; font-size: .78rem; color: var(--text-dim); line-height: 1.3; }
.stage-legend-dot { align-self: center; }
.stage-legend-row .mono { margin-left: auto; color: var(--text); font-size: .76rem; flex: none; }
.stage-legend-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }

.stage-role-completed, .stage-role-funded, .stage-legend-dot.stage-role-completed, .stage-legend-dot.stage-role-funded { stroke: var(--verified); background: var(--verified); }
.stage-role-construction, .stage-role-procurement, .stage-legend-dot.stage-role-construction, .stage-legend-dot.stage-role-procurement { stroke: var(--med); background: var(--med); }
.stage-role-design, .stage-role-review, .stage-role-planning, .stage-legend-dot.stage-role-design, .stage-legend-dot.stage-role-review, .stage-legend-dot.stage-role-planning { stroke: var(--low-ink); background: var(--low-ink); }
.stage-role-identified, .stage-role-unknown, .stage-legend-dot.stage-role-identified, .stage-legend-dot.stage-role-unknown { stroke: var(--low); background: var(--low); }

@media (max-width: 480px) {
  .stage-donut-wrap { flex-direction: column; align-items: flex-start; }
  .stage-donut { width: 148px; height: 148px; }
}

/* Airport Detail (BOS visual alignment): header row (title/subtitle left,
   the airport's one dominant project status - Mission #1's
   Airport.primary_signal - as a large badge top-right), a quick-facts
   strip now sitting directly under the header as its own lightweight row
   (not boxed inside project-summary - V2's own "quick-fact strip
   integrated into header area"), and a real 3-column body row
   (Projektsammanfattning / Banor / EMAS idag) replacing the initial
   pass's 2-column + full-width-table stack - "fewer giant stacked
   rectangles, make better use of desktop width". */
.airport-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; margin-bottom: 4px; }
.airport-header h1 { margin-bottom: 4px; }
.airport-flag { font-size: 1.3rem; vertical-align: -2px; }
.hero-status-lg { font-size: 1.05rem; padding: 9px 20px; white-space: nowrap; }

.quick-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 32px;
  margin: 18px 0 32px;
  padding: 16px 0;
  border-top: 1px solid rgba(139,150,172,.2);
  border-bottom: 1px solid rgba(139,150,172,.2);
}
.quick-fact { display: flex; flex-direction: column; gap: 5px; }
.quick-fact-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-faint); }
.quick-fact-value { font-weight: 500; }

/* "V2.1 Final Visual Polish" correction 1: removed the former
   `.airport-body-grid > .card { height: 100% }` rule. With
   `align-items: start` (grid's own default sizing per item, no stretch)
   and no explicit height, each card now sizes to its own real content -
   the fix for correction 2 below (a card with only an honest empty-state
   message no longer stretches to match its taller sibling's height). */
.airport-body-grid { display: grid; grid-template-columns: minmax(0,1.3fr) minmax(0,1fr) minmax(0,1fr); gap: 22px; align-items: start; }
.runway-emas-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }

/* "V2.1 Final Visual Polish" correction 2: a compact empty-state modifier
   (generic - not tied to any specific airport/card) for the two
   physical-data cards (Banor, EMAS idag) in .airport-body-grid, so a
   missing-data explanation reads as brief secondary information instead
   of visually dominating the row. The base .empty-state class (used
   elsewhere on the site - Tidslinje, Projekt, historik) is untouched. */
.empty-state-compact { padding: 20px 16px; font-size: .86rem; }

@media (max-width: 980px) {
  .airport-body-grid { grid-template-columns: 1fr; }
  .runway-emas-grid { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  .runway-emas-grid { grid-template-columns: 1fr; }
  .hero-status-lg { font-size: .92rem; padding: 7px 16px; }
  .quick-facts { gap: 20px; }
}

/* ---------------------------------------------------------------------
   "RWI - Juicy Design Mission #2 - V2.3" mission: Global intelligens
   (country-level radar/grid map, never real map geometry/coordinates -
   see build.py's own geo-recon docstring) + Viktiga utvecklingar. */

/* Internal depth for the map card - a soft radial wash behind the SVG,
   distinct from the flat .panel-strong background every other card uses,
   so this row reads as an integrated intelligence surface rather than
   "two more identical boxes" (Section 9's own instruction). */
.map-card { position: relative; overflow: hidden; }
.map-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 60% at 50% 42%, rgba(242,183,5,.06), transparent 72%);
  pointer-events: none;
}
.map-card .card-body { position: relative; }

/* "V2.4 Global Intelligence Map WOW Pass": a real, recognizable world land
   silhouette (see app/static_export/geo/PROVENANCE.md) - low-contrast
   navy/slate fill so it reads immediately as "world map" without
   competing with the gold activity nodes drawn over it. */
.global-map { display: block; width: 100%; height: auto; min-height: 220px; background: radial-gradient(ellipse 90% 85% at 50% 45%, rgba(19,27,44,.5), transparent 75%); border-radius: var(--radius-sm); }
.map-land { fill: #26344c; stroke: #34455f; stroke-width: .6; }

/* Real, proportional activity nodes - radius/glow already computed from
   real pct_of_total in build.py (sqrt-scaled for fair perceived area).
   Non-dominant markets get a visible-but-restrained fill; the dominant
   market gets full accent saturation plus its own radial glow circle
   (rendered separately, underneath, in the template) - "strongest market
   gets strongest glow", never every active country the same treatment. */
.map-node { fill: rgba(242,183,5,.55); stroke: rgba(242,183,5,.85); stroke-width: 1.2; }
.map-node-dominant { fill: var(--accent); stroke: #fff; stroke-opacity: .6; stroke-width: 1.5; }

.map-legend { display: flex; flex-wrap: wrap; gap: 10px 22px; margin-top: 18px; padding-top: 16px; border-top: 1px solid rgba(139,150,172,.18); }
.map-legend-item { display: flex; align-items: baseline; gap: 7px; font-size: .84rem; color: var(--text-dim); }
.map-legend-item-dominant { color: var(--text); font-weight: 500; }
.map-legend-flag { font-size: 1rem; }
.map-legend-country { white-space: nowrap; }
.map-legend-pct { color: var(--accent); font-weight: 600; }
.map-legend-count { color: var(--text-faint); font-size: .78rem; }

/* ("RWI - Mission #23C" mission) Global Footprint mode toggle - pure CSS,
   no JavaScript required. Two real, focusable, labelled <input
   type="radio"> elements (visually minimized, never display:none, so
   keyboard/screen-reader access is unaffected) drive which
   .footprint-panel is shown via the :checked ~ sibling combinator - see
   index.html's own comment for the exact markup this depends on. */
.footprint-radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); overflow: hidden;
}
.footprint-toggle { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.footprint-toggle-label {
  padding: 7px 16px;
  border-radius: 999px;
  border: 1px solid rgba(139,150,172,.28);
  color: var(--text-dim);
  font-size: .84rem;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
}
#footprint-mode-installed:checked ~ .footprint-toggle .footprint-toggle-label-installed,
#footprint-mode-current:checked ~ .footprint-toggle .footprint-toggle-label-current {
  background: var(--accent);
  color: #0b0f1a;
  border-color: var(--accent);
}
#footprint-mode-installed:focus-visible ~ .footprint-toggle .footprint-toggle-label-installed,
#footprint-mode-current:focus-visible ~ .footprint-toggle .footprint-toggle-label-current {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.footprint-panel { display: none; }
#footprint-mode-installed:checked ~ .footprint-panel-installed { display: block; }
#footprint-mode-current:checked ~ .footprint-panel-current { display: block; }

.footprint-explain { margin: 0 0 14px; font-size: .86rem; color: var(--text-dim); }
.footprint-disclosure { margin: 14px 0 0; font-size: .78rem; color: var(--text-faint); font-style: italic; }
.map-node-installed { fill: rgba(94,180,142,.55); stroke: rgba(94,180,142,.9); stroke-width: 1.2; }

.footprint-country-list { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(139,150,172,.18); }

/* ("RWI - Mission #24B" mission) Country drill-down - native
   <details>/<summary>, no JavaScript. .footprint-country-header is now
   the <summary> itself (previously a plain <div>), so its own UA default
   `display: list-item` must be overridden back to the pre-#24B flex
   layout; the native disclosure triangle is replaced with a custom
   ::before chevron (toggled by the browser's own `open` attribute, no JS)
   so the affordance stays visually consistent across browsers. */
.footprint-country { padding: 8px 0; }
summary.footprint-country-header {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  padding: 6px 4px;
  margin: -6px -4px 8px;
  border-radius: 8px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
summary.footprint-country-header::-webkit-details-marker { display: none; }
summary.footprint-country-header::before {
  content: "▸";
  color: var(--text-faint);
  font-size: .72rem;
  width: 1em;
}
details[open] > summary.footprint-country-header::before { content: "▾"; }
summary.footprint-country-header:hover { background: rgba(139,150,172,.08); }
summary.footprint-country-header:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.footprint-country-count { color: var(--text-dim); font-size: .82rem; }
.footprint-no-marker { color: var(--text-faint); font-size: .76rem; font-style: italic; }
.footprint-airport-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.footprint-airport-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  padding: 8px 0;
  border-bottom: 1px solid rgba(139,150,172,.1);
  font-size: .84rem;
}
.footprint-airport-list li:last-child { border-bottom: none; }
.footprint-airport-list li a { color: var(--text); text-decoration: none; font-weight: 500; }
.footprint-airport-list li a:hover { text-decoration: underline; }
.footprint-tag {
  font-size: .74rem;
  color: var(--text-faint);
  background: rgba(139,150,172,.1);
  border-radius: 999px;
  padding: 2px 9px;
}
.footprint-tag-confirmed { color: var(--accent); background: rgba(242,183,5,.12); }

/* Utveckling (renamed from "Viktiga utvecklingar" in V2.4 - the longer
   heading wrapped onto two lines at desktop width) - compact clickable
   rows, deliberately denser than Signalöversikt's own table (this is a
   short, high-signal companion list, never a second full table). */
.important-developments { display: flex; flex-direction: column; gap: 2px; }
.important-dev-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 4px;
  border-bottom: 1px solid rgba(139,150,172,.14);
  color: inherit;
  text-decoration: none;
}
.important-developments a.important-dev-row:hover { background: rgba(255,255,255,.02); text-decoration: none; }
.important-developments .important-dev-row:last-child { border-bottom: none; }
.important-dev-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.important-dev-airport { font-size: .76rem; color: var(--text-faint); }
.important-dev-title { font-size: .92rem; color: var(--text); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.important-dev-year { flex: none; color: var(--text-dim); font-size: .85rem; }

@media (max-width: 480px) {
  .global-map { min-height: 160px; }
  .map-legend { gap: 8px 16px; }
  .important-dev-title { white-space: normal; }
  .footprint-toggle-label { padding: 8px 14px; }
  .footprint-airport-list li { font-size: .8rem; }
  summary.footprint-country-header { padding: 10px 4px; }
}

/* ---------------------------------------------------------------------
   "RWI - Juicy Design Mission #3" mission: Airport Detail "project
   intelligence" phase-progression strip + "Plats" location panel. */

/* Phase-progression strip - one connected row of 6 dots/labels, built
   entirely from build.py's own deterministic _project_phase_view()
   (real status_role only, never invented). Past/current segments read as
   a "traversed" gold path; future segments stay dim - the visual answer
   to "where in the process is this," distinct from (and never competing
   with) the header's own single dominant phase badge. */
.phase-strip-scroll { overflow-x: auto; }
.phase-strip { display: flex; align-items: flex-start; min-width: 480px; padding: 6px 0 2px; }
.phase-step { flex: 1; position: relative; display: flex; flex-direction: column; align-items: center; gap: 9px; text-align: center; }
.phase-step:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 6px;
  left: 50%;
  width: 100%;
  height: 2px;
  background: rgba(139,150,172,.25);
  z-index: 0;
}
.phase-step-past:not(:last-child)::after,
.phase-step-current:not(:last-child)::after { background: rgba(242,183,5,.55); }
.phase-dot { width: 13px; height: 13px; border-radius: 50%; background: var(--panel2); border: 2px solid rgba(139,150,172,.35); position: relative; z-index: 1; }
.phase-step-past .phase-dot { background: var(--accent); border-color: var(--accent); opacity: .55; }
.phase-step-current .phase-dot { width: 17px; height: 17px; background: var(--accent); border-color: #fff; box-shadow: 0 0 0 5px rgba(242,183,5,.22); }
.phase-label { font-size: .76rem; color: var(--text-dim); white-space: nowrap; }
.phase-step-current .phase-label { color: var(--text); font-weight: 600; }
.phase-step-future .phase-label { color: var(--text-faint); }

/* "Plats" - the exact same .global-map/.map-land styling Overview's
   Global intelligens already established (Mission #2), sized more
   compactly here since this is a secondary supporting panel, not the
   page's own primary visual centerpiece. */
.location-body { display: flex; flex-direction: column; gap: 10px; }
.location-map { max-height: 220px; }
.location-coords { color: var(--text-dim); font-size: .82rem; text-align: right; margin: 0; }

@media (max-width: 480px) {
  .phase-strip { min-width: 420px; }
  .location-map { max-height: 170px; }
}
