/**
 * apps/web/live/unified.css
 *
 * The unified app (served at /). Two jobs:
 *  1. Turn the /live dashboard from a fixed full-viewport app into one scrolling
 *     page: identity hero on top, then the full explorer as a tall section.
 *  2. Style the editorial "national situation" hero + the "your area" lens
 *     (the home's look the team likes), populated by situation-hero.js.
 * Scoped to body.unified-app so the standalone /live shell is unaffected.
 */

/* ── Scrolling page (override /live's fixed-viewport flex-column body) ──────── */
body.unified-app {
  display: block;            /* /live's body is flex-column; block lets the page scroll */
  height: auto;
  overflow-x: hidden;
  overflow-y: auto;
  /* Restore the custom type the home used — live-civic overrides these to
     system fonts; the unified app wants Inter (body) + Fraunces (display). */
  /* The unified app is Inter end-to-end (the hero word uses an explicit system
     grotesk of its own). Fraunces is no longer used here, so we don't load it. */
  --font-display: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-family: var(--font-body);
}
body.unified-app .dashboard-header {
  position: sticky;
  top: 0;
  z-index: 60;
}
/* The explorer is a viewport-tall section (its sidebar feed + map scroll inside),
   so it still feels like the whole app once you scroll past the hero. */
body.unified-app .dashboard-main {
  height: calc(100vh - var(--header-height, 64px));
  min-height: 560px;
  overflow: hidden;
}

/* ── Identity hero (national situation) ────────────────────────────────────── */
.unified-hero {
  background: var(--civic-green, #0C3B2E);
  color: var(--civic-paper, #F4F1E8);
  padding: 40px 24px 30px;
}
/* The hidden attribute must win over the explicit display:flex on hero blocks
   (.uh-actions/.uh-impact/.uh-controls/.uh-digest/.uh-locate), or they'd show
   empty. */
.unified-hero [hidden] { display: none !important; }
.uh-inner {
  max-width: 760px;
  margin: 0 auto;
  text-align: center;
}

/* National brief — amber card that rides above the hero when something is
   active nationally while your own area is calm (escalates to red if urgent). */
.uh-natbrief {
  text-align: left;
  margin: 0 auto 22px;
  border: 1px solid rgba(242, 181, 68, 0.34);
  border-radius: 14px;
  overflow: hidden;
}
.uh-natbrief .nb-top {
  display: flex; align-items: center; gap: 9px; padding: 10px 15px;
  background: rgba(242, 181, 68, 0.13); border-bottom: 1px solid rgba(242, 181, 68, 0.2);
}
.uh-natbrief .nb-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--civic-amber, #E8A11C); flex: none; }
.uh-natbrief .nb-tag { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; font-weight: 700; color: var(--civic-amber, #E8A11C); }
.uh-natbrief .nb-meta { margin-left: auto; color: var(--civic-paper-mut, #B9C9C0); font-size: 12px; }
.uh-natbrief .nb-body { padding: 13px 16px 15px; }
.uh-natbrief .nb-head { font-size: 16px; font-weight: 700; letter-spacing: -0.01em; margin: 0; }
.uh-natbrief .nb-sub { margin: 6px 0 0; color: var(--civic-paper-mut, #B9C9C0); font-size: 13.5px; line-height: 1.55; }
/* Feedback sits OUTSIDE .nb-body, so give it the card's own inset instead of
   letting it hug the left/bottom border. */
.uh-natbrief .ai-feedback-controls { padding: 0 16px 14px; margin-top: -2px; }
.uh-natbrief.is-urgent { border-color: rgba(240, 59, 32, 0.4); }
.uh-natbrief.is-urgent .nb-top { background: rgba(240, 59, 32, 0.14); border-bottom-color: rgba(240, 59, 32, 0.22); }
.uh-natbrief.is-urgent .nb-dot { background: #FF6A3D; }
.uh-natbrief.is-urgent .nb-tag { color: #FF6A3D; }

/* Eyebrow: national label, or a clickable place pill once located. */
.uh-kicker {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--civic-ok, #7BE3B1);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 18px;
}
.uh-kicker.is-urgent { color: #FF6A3D; }
.uh-place-toggle {
  appearance: none; background: transparent; border: 0; cursor: pointer; padding: 0;
  color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit;
  display: inline-flex; align-items: center; gap: 7px;
}
.uh-place-toggle .uh-pin { width: 13px; height: 13px; color: var(--civic-ok, #7BE3B1); }
.uh-place-toggle .uh-pin svg { width: 100%; height: 100%; }
.uh-place-toggle .uh-kicker-cv { color: var(--civic-paper-mut, #B9C9C0); }
.uh-place-toggle:hover { color: var(--civic-paper, #F4F1E8); }
/* The signature hero word: heavy uppercase sans with the second word outlined
   (e.g. ALL / GOOD), matching the original home. Two-tone via .uh-stroke. */
.uh-status-word {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900;
  font-size: clamp(54px, 12vw, 132px);
  line-height: 0.9;
  letter-spacing: -0.05em;
  text-transform: uppercase;
  margin: 8px 0 12px;
  color: var(--civic-paper, #F4F1E8);
}
.uh-status-word .uh-stroke {
  color: transparent;
  -webkit-text-stroke: 2.5px var(--civic-paper, #F4F1E8);
}
/* Three severity levels for the signature word: calm (cream, base above),
   watch (amber — HEADS UP), urgent (red — TAKE CARE). */
.uh-status-word.is-watch { color: #F5A623; }
.uh-status-word.is-watch .uh-stroke { -webkit-text-stroke-color: #F5A623; }
.uh-status-word.is-urgent { color: #FF6A3D; }
.uh-status-word.is-urgent .uh-stroke { -webkit-text-stroke-color: #FF6A3D; }
.uh-skel { opacity: 0.25; }
.uh-brief-big {
  margin: 0 auto 4px;
  font-size: clamp(17px, 2.2vw, 21px);
  font-weight: 700;
  letter-spacing: -0.01em;
  max-width: 34ch;
}
.uh-brief-big b { font-weight: 800; }
.uh-brief-sub {
  margin: 6px auto 0;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--civic-paper-mut, #B9C9C0);
  max-width: 52ch;
}
/* Sub-line carrying an agent-written brief + its provenance marker. The copy
   itself stays quiet (same muted paper); only the small marker signals AI. */
.uh-agent-mark {
  display: block;
  margin-top: 8px;
  font-size: 10.5px;
  letter-spacing: 0.9px;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--civic-ok, #7BE3B1);
  opacity: 0.9;
}
.uh-agent-mark::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  margin-right: 6px;
  vertical-align: 1px;
}
.uh-agent-mark[hidden] { display: none; }

/* Hero brief feedback centres under the centred hero text (default/mobile
   layout); the desktop rail layout below left-aligns it with the actions.
   The natbrief CARD control keeps its own left inset (styled separately). */
#uh-brief-feedback { justify-content: center; }

/* AI-first thinking loader: while the cell agent generates, the sub-line is a
   calm pulsing mark + a live line naming what the agent is doing right now
   (fed by situation_progress frames). One subtle pulse, no chrome. */
.uh-agent-progress {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--civic-ok, #7BE3B1);
}
.uh-agent-progress .ap-orb {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  animation: ap-pulse 1.6s ease-in-out infinite;
}
.uh-agent-progress .ap-text {
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.2px;
  opacity: 0.92;
}
@keyframes ap-pulse {
  0%, 100% { transform: scale(1); opacity: 0.55; box-shadow: 0 0 0 0 currentColor; }
  50% { transform: scale(1.25); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .uh-agent-progress .ap-orb { animation: none; opacity: 0.9; }
}

/* Severe: actions + an honest "what it means for you" line. */
.uh-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 18px; }
.uh-actions .btn-red {
  appearance: none; background: #F03B20; color: #fff; border: 0; font: inherit;
  font-weight: 600; font-size: 14px; padding: 11px 20px; border-radius: 999px; cursor: pointer;
}
.uh-actions .btn-red:hover { opacity: 0.92; }
.uh-actions .btn-ghost {
  appearance: none; background: transparent; border: 1px solid rgba(244, 241, 232, 0.3);
  color: var(--civic-paper, #F4F1E8); font: inherit; font-size: 14px; padding: 11px 18px; border-radius: 999px; cursor: pointer;
}
.uh-actions .btn-ghost:hover { background: rgba(244, 241, 232, 0.1); }
.uh-impact {
  display: flex; align-items: center; gap: 12px; text-align: left;
  max-width: 560px; margin: 18px auto 0; padding: 12px 16px;
  border: 1px solid rgba(244, 241, 232, 0.14); border-radius: 14px;
}
.uh-impact .uh-pin { width: 16px; height: 16px; color: var(--civic-ok, #7BE3B1); flex: none; }
.uh-impact .uh-pin svg { width: 100%; height: 100%; }
.uh-impact .lab { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: var(--civic-paper-mut, #B9C9C0); }
.uh-impact .txt { font-size: 14.5px; font-weight: 600; }
.uh-impact .more { margin-left: auto; color: var(--civic-ok, #7BE3B1); font-size: 13px; white-space: nowrap; cursor: pointer; }

/* Stat strip — centered, four bordered cells. */
.uh-stats {
  display: flex;
  justify-content: center;
  margin: 30px auto 0;
  max-width: 580px;
  border: 1px solid rgba(244, 241, 232, 0.16);
  border-radius: 14px;
  overflow: hidden;
}
.uh-stat {
  flex: 1;
  min-width: 0;
  padding: 15px 8px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}
.uh-stat + .uh-stat { border-left: 1px solid rgba(244, 241, 232, 0.10); }
.uh-stat b {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: clamp(20px, 3.4vw, 28px);
  line-height: 1;
  font-weight: 900;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--civic-paper, #F4F1E8);
}
.uh-stat b small { font-size: 12px; color: var(--civic-paper-mut, #B9C9C0); font-weight: 600; }
.uh-stat b.warn { color: var(--civic-amber, #E8A11C); }
.uh-stat b.hot { color: var(--civic-hot, #FF7A45); }
.uh-stat b.ok { color: var(--civic-ok, #7BE3B1); }
.uh-stat-l {
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--civic-paper-mut, #B9C9C0);
  text-align: center;
}
/* B shaking tile: plain-language word + level pips (instead of "IV"). */
.uh-shake-word {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: clamp(17px, 2.6vw, 22px);
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1;
}
/* Pips flex to share the cell width (never a fixed 87px that overruns the
   divider on narrow phones); capped so they don't balloon on wide cells. */
.uh-pips { display: flex; gap: 3px; width: 100%; max-width: 92px; }
.uh-pips i { flex: 1; min-width: 0; height: 6px; border-radius: 2px; background: rgba(244, 241, 232, 0.12); }

/* ── Location finder (first-visit invite + change picker) ──────────────────── */
.uh-pin svg { width: 100%; height: 100%; }
.uh-locate { max-width: 440px; margin: 22px auto 0; }
.uh-locate-lead { margin: 0 0 12px; font-size: 14px; color: var(--civic-paper-mut, #B9C9C0); }
.uh-ny-pick { display: flex; gap: 8px; flex-wrap: wrap; }
.uh-ny-pick input {
  flex: 1; min-width: 180px; background: rgba(244, 241, 232, 0.06);
  border: 1px solid rgba(244, 241, 232, 0.18); border-radius: 12px; padding: 12px 14px;
  color: var(--civic-paper, #F4F1E8); font: inherit; font-size: 14px;
}
.uh-ny-pick input::placeholder { color: var(--civic-paper-mut, #B9C9C0); }
.uh-ny-gps {
  appearance: none; background: var(--civic-ok, #7BE3B1); color: #0A3328; border: 0;
  border-radius: 12px; padding: 12px 18px; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer;
}
.uh-ny-gps:hover { opacity: 0.92; }
.uh-ny-sugg { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; text-align: left; }
.uh-ny-sugg button {
  appearance: none; text-align: left; background: rgba(244, 241, 232, 0.06);
  border: 1px solid rgba(244, 241, 232, 0.14); border-radius: 10px;
  padding: 9px 12px; cursor: pointer; color: var(--civic-paper, #F4F1E8); font: inherit;
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
}
.uh-ny-sugg button:hover:not([disabled]) { background: rgba(244, 241, 232, 0.12); }
.uh-ny-sugg button[disabled] { opacity: 0.6; cursor: default; }
.uh-ny-sugg .s-main { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.uh-ny-sugg .s-name { font-size: 14px; }
.uh-ny-sugg .s-kind { font-size: 11px; color: var(--civic-paper-mut, #B9C9C0); text-transform: uppercase; letter-spacing: 0.3px; }
.uh-ny-sugg .s-meta { font-size: 11.5px; color: var(--civic-paper-mut, #B9C9C0); white-space: nowrap; }

/* Located: quiet controls row. */
.uh-controls { display: flex; gap: 14px; justify-content: center; align-items: center; flex-wrap: wrap; margin-top: 20px; }
.uh-ctl { appearance: none; background: transparent; border: 0; font: inherit; font-size: 14px; cursor: pointer; padding: 0; color: var(--civic-paper-mut, #B9C9C0); }
.uh-ctl#uh-ny-change { color: var(--civic-ok, #7BE3B1); border-bottom: 1px solid rgba(123, 227, 177, 0.4); padding-bottom: 1px; }
.uh-ctl:hover { color: var(--civic-paper, #F4F1E8); }
.uh-ctl-sep { color: rgba(244, 241, 232, 0.4); }

/* Daily-email signup. */
.uh-digest { display: flex; gap: 8px; margin: 14px auto 0; max-width: 380px; flex-wrap: wrap; justify-content: center; }
.uh-digest input {
  flex: 1; min-width: 180px; background: rgba(244, 241, 232, 0.06);
  border: 1px solid rgba(244, 241, 232, 0.18); border-radius: 12px; padding: 11px 14px;
  color: var(--civic-paper, #F4F1E8); font: inherit; font-size: 14px;
}
.uh-digest input::placeholder { color: var(--civic-paper-mut, #B9C9C0); }
.uh-digest button {
  appearance: none; background: var(--civic-ok, #7BE3B1); color: #0A3328; border: 0;
  border-radius: 12px; padding: 11px 18px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.uh-digest button:hover { opacity: 0.92; }
.uh-digest-msg { margin: 10px auto 0; font-size: 12.5px; color: var(--civic-ok, #7BE3B1); max-width: 52ch; }

.uh-scrollcue {
  display: block; text-align: center; margin: 26px auto 0; max-width: 1180px;
  font-size: 12px; letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--civic-paper-mut, #B9C9C0); text-decoration: none;
}
.uh-scrollcue:hover { color: var(--civic-paper, #F4F1E8); }
/* Two wordings: "scroll down" on desktop, "swipe right" on the phone deck. */
.uh-scrollcue .cue-mobile { display: none; }
@media (max-width: 899px) {
  .uh-scrollcue .cue-desktop { display: none; }
  .uh-scrollcue .cue-mobile { display: inline; }
}

/* ── Desktop ≥1200px: two-column hero (situation left, live rail right) ──────── */
/* The centered 760px column left ~1/3 of the screen empty on each side. Wide
   viewports now pair it with a live "Happening now" rail so the first screen
   carries real information edge to edge. Below 1200px nothing changes. */
.uh-rail { display: none; }
@media (min-width: 1200px) {
  body.unified-app .unified-hero { padding: 34px 48px 24px; }
  body.unified-app .uh-cols {
    display: flex;
    align-items: center;
    gap: 64px;
    max-width: 1200px;
    margin: 0 auto;
  }
  body.unified-app .uh-inner { flex: 1 1 auto; min-width: 0; text-align: left; }
  body.unified-app .uh-kicker { justify-content: flex-start; }
  body.unified-app .uh-brief-big,
  body.unified-app .uh-brief-sub,
  body.unified-app .uh-locate,
  body.unified-app .uh-impact,
  body.unified-app .uh-stats,
  body.unified-app .uh-digest { margin-left: 0; margin-right: 0; }
  body.unified-app .uh-actions,
  body.unified-app .uh-controls,
  body.unified-app #uh-brief-feedback { justify-content: flex-start; }
  body.unified-app .uh-digest { justify-content: flex-start; }
  body.unified-app .uh-natbrief { margin-left: 0; margin-right: 0; }
  body.unified-app .uh-stats { margin-top: 26px; }

  body.unified-app .uh-rail {
    display: block;
    flex: 0 0 390px;
    min-width: 0;
  }
}
.uh-rail-head {
  display: flex; align-items: baseline; gap: 10px;
  margin: 0 0 10px; padding: 0 2px;
}
.uh-rail-head .rh-tag {
  font-size: 11px; letter-spacing: 1.2px; text-transform: uppercase; font-weight: 700;
  color: var(--civic-paper-mut, #B9C9C0);
}
.uh-rail-head .rh-link {
  margin-left: auto; font-size: 12.5px; color: var(--civic-ok, #7BE3B1);
  text-decoration: none; white-space: nowrap;
}
.uh-rail-head .rh-link:hover { color: var(--civic-paper, #F4F1E8); }
.uh-rail-list { display: flex; flex-direction: column; gap: 8px; }
.uh-rail-item {
  appearance: none; font: inherit; cursor: pointer; width: 100%; text-align: left;
  display: flex; align-items: center; gap: 12px;
  background: rgba(244, 241, 232, 0.05);
  border: 1px solid rgba(244, 241, 232, 0.14);
  border-radius: 14px;
  padding: 12px 14px;
  color: var(--civic-paper, #F4F1E8);
  transition: background 0.15s ease, border-color 0.15s ease;
}
.uh-rail-item:hover { background: rgba(244, 241, 232, 0.1); border-color: rgba(244, 241, 232, 0.24); }
.uh-rail-item .ri-icon {
  flex: none; width: 34px; height: 34px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
}
.uh-rail-item .ri-icon svg { width: 18px; height: 18px; }
.uh-rail-item .ri-body { min-width: 0; flex: 1; }
.uh-rail-item .ri-head {
  margin: 0; font-size: 14px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.uh-rail-item .ri-meta {
  margin: 3px 0 0; font-size: 12px; color: var(--civic-paper-mut, #B9C9C0);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.uh-rail-item .ri-felt {
  flex: none;
  align-self: center;
  border: 1px solid rgba(244, 241, 232, 0.16);
  border-radius: 999px;
  padding: 3px 7px;
  color: var(--civic-paper-mut, #B9C9C0);
  background: rgba(244, 241, 232, 0.05);
  font-size: 10.5px;
  font-weight: 650;
  line-height: 1;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.uh-rail-quiet {
  border: 1px solid rgba(244, 241, 232, 0.14); border-radius: 14px;
  padding: 16px; font-size: 13.5px; color: var(--civic-paper-mut, #B9C9C0);
}
.uh-rail-quiet b { color: var(--civic-ok, #7BE3B1); font-weight: 700; }

/* ── Site-wide emergency banner ─────────────────────────────────────────────── */
/* Pinned red bar for active life-safety / evacuation alerts. First element in the
   body, sticky to the top so it stays visible while scrolling on desktop; on the
   mobile deck the deck height is trimmed by --eb-h (see has-emergency rule). */
:root { --eb-h: 46px; }
.emergency-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--eb-h);
  padding: 8px 16px;
  position: sticky;
  top: 0;
  z-index: 300;
  background: #B00610;
  color: #fff;
  font-size: 14px;
  line-height: 1.3;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}
.emergency-banner[hidden] { display: none; }
.emergency-banner .eb-badge {
  flex: none;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(255, 255, 255, 0.22);
  padding: 3px 8px;
  border-radius: 4px;
}
.emergency-banner .eb-text {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.emergency-banner .eb-more { opacity: 0.85; font-weight: 600; }
.emergency-banner .eb-cta {
  flex: none;
  color: #fff;
  font-weight: 800;
  text-decoration: underline;
  white-space: nowrap;
}

/* Top swipe-deck pager (segment progress bar): hidden on desktop, shown on phones. */
.uh-pager { display: none; }
/* Map-panel swipe handle: phone deck only. */
.deck-grab { display: none; }

/* ── Mobile: horizontal swipe deck ─────────────────────────────────────────── */
/* Phones get the swipe-deck the team liked: hero / feed / map as full-screen
   panels you swipe between (native CSS scroll-snap). A thin top segment bar
   (.uh-pager) lights the active segment amber to show the current page, and the
   bottom action bar tabs sync to the active panel (mobile-deck.js). Desktop is
   unaffected. */
@media (max-width: 899px) {
  .unified-hero { padding: 22px 16px 16px; }
  .uh-status-word { font-size: clamp(46px, 16vw, 84px); }
  .uh-stats { max-width: none; }
  .uh-natbrief { margin-bottom: 16px; }

  body.unified-app { height: 100dvh; overflow: hidden; }

  /* Top segment progress bar — one thin track per panel, active one lit. */
  body.unified-app .uh-pager {
    display: flex;
    gap: 4px;
    padding: 9px 16px 7px;
    background: var(--civic-green, #0C3B2E);
    position: sticky;
    top: var(--header-height, 64px);
    z-index: 55;
  }
  body.unified-app .uh-pager .seg {
    flex: 1;
    height: 3px;
    border-radius: 2px;
    background: rgba(244, 241, 232, 0.18);
    transition: background 0.2s ease;
  }
  body.unified-app .uh-pager .seg.on { background: var(--civic-amber, #E8A11C); }

  body.unified-app #app-deck {
    display: flex;
    flex-flow: row nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    /* Subtract the pager bar (~19px) as well as header + bottom action bar. */
    height: calc(100dvh - var(--header-height) - var(--civic-actionbar-h, 56px) - 19px);
    scrollbar-width: none;
  }
  body.unified-app #app-deck::-webkit-scrollbar { display: none; }
  /* When the emergency banner is pinned it eats vertical space above the deck —
     trim the deck height so its panels still fit the viewport exactly. */
  body.unified-app.has-emergency #app-deck {
    height: calc(100dvh - var(--header-height) - var(--civic-actionbar-h, 56px) - 19px - var(--eb-h, 46px));
  }

  /* Panels are sized to the deck's content box (100%), NOT the viewport (100vw).
     100vw includes the vertical-scrollbar gutter, so on any browser with
     persistent scrollbars each panel ends up ~15px wider than the visible area,
     clipping the right edge and forcing a stray horizontal scrollbar. The deck
     is a flex child of <body>, so its content width already excludes the
     scrollbar — 100% is the correct, gutter-safe basis. */
  body.unified-app #unified-hero {
    flex: 0 0 100%; width: 100%; height: 100%;
    overflow-y: auto; scroll-snap-align: start;
    /* Balance the panel vertically: content floats to the middle, the swipe cue
       sits near the bottom — no ~180px slab of empty green above the tab bar.
       Auto margins collapse gracefully when the content is taller than the panel. */
    display: flex; flex-direction: column;
  }
  body.unified-app #unified-hero .uh-cols { margin-block: auto; }
  body.unified-app #unified-hero .uh-scrollcue { margin-top: 22px; padding-bottom: 10px; }
  /* Promote the feed + map out of dashboard-main so they become deck panels. */
  body.unified-app #app-deck > #dashboard-main { display: contents; }
  body.unified-app #app-deck .sidebar-panel {
    flex: 0 0 100%; width: 100%; min-width: 100%; height: 100%;
    scroll-snap-align: start;
    display: flex !important;   /* defeat /live's data-panel show/hide */
  }
  body.unified-app #app-deck .map-viewport {
    flex: 0 0 100%; width: 100%; min-width: 100%; height: 100%;
    scroll-snap-align: start;
    display: block !important;
  }
  /* The explorer section override is moot on mobile (dashboard-main has no box). */
  body.unified-app .dashboard-main { height: auto; overflow: visible; }

  /* Swipe handle over the map panel. MapLibre's canvas captures every touch for
     pan/zoom, so this strip is the only place a swipe can grab the deck itself.
     touch-action: pan-x hands horizontal gestures straight to the deck scroller. */
  body.unified-app .map-viewport { position: relative; }
  body.unified-app .deck-grab {
    position: absolute; top: 0; left: 0; right: 0; z-index: 30;
    display: flex; align-items: center; justify-content: center; gap: 8px;
    height: 26px;
    background: rgba(12, 59, 46, 0.82);
    color: rgba(244, 241, 232, 0.65);
    touch-action: pan-x;
  }
  body.unified-app .deck-grab i {
    width: 26px; height: 4px; border-radius: 2px;
    background: rgba(244, 241, 232, 0.35);
  }
  body.unified-app .deck-grab .dg-lab {
    font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; font-weight: 700;
  }
  body.unified-app .deck-grab .dg-chev { font-size: 13px; line-height: 1; }
  /* Clear the strip: the map's own top-anchored chrome starts below it. */
  body.unified-app .map-viewport .source-disclosure { top: 38px; }
  body.unified-app .map-viewport .map-legend { top: 82px; }
  body.unified-app .map-viewport .maplibregl-ctrl-top-right .maplibregl-ctrl { margin-top: 36px; }

  /* Active tab in the bottom bar: the old paper-vs-muted colour shift was too
     subtle to read at a glance — give the active tab a quiet pill as well. */
  body.unified-app .live-actionbar button { border-radius: 12px; transition: background 0.15s ease, color 0.15s ease; }
  body.unified-app .live-actionbar .on {
    color: var(--civic-paper, #F4F1E8);
    background: rgba(244, 241, 232, 0.12);
  }
}

/* ── Hazard-aware modal section order ───────────────────────────────────────── */
/* .modal-body is a flex column; reorder its sections per hazard so the modal
   leads with what matters. Default (quake/tsunami/volcano/road) keeps source
   order (hero on top). data-hazard is set by details.js populateModal. */
#alert-modal[data-hazard="emergency"] #modal-actions-section { order: 1; }
#alert-modal[data-hazard="emergency"] .description-card       { order: 2; }
#alert-modal[data-hazard="emergency"] #modal-chat-entry       { order: 3; }
#alert-modal[data-hazard="emergency"] #modal-chat-share-btn   { order: 4; }
#alert-modal[data-hazard="emergency"] #modal-telemetry-hud    { order: 5; }
#alert-modal[data-hazard="emergency"] #modal-news-section     { order: 6; }
#alert-modal[data-hazard="emergency"] .attribution-row        { order: 7; }

#alert-modal[data-hazard="weather"] #modal-hero              { order: 1; }
#alert-modal[data-hazard="weather"] .description-card        { order: 2; }
#alert-modal[data-hazard="weather"] #modal-telemetry-hud     { order: 3; }
#alert-modal[data-hazard="weather"] #modal-cameras-container { order: 4; }
#alert-modal[data-hazard="weather"] #modal-actions-section   { order: 5; }
#alert-modal[data-hazard="weather"] #modal-chat-entry        { order: 6; }
#alert-modal[data-hazard="weather"] #modal-chat-share-btn    { order: 7; }
#alert-modal[data-hazard="weather"] #modal-news-section      { order: 8; }
#alert-modal[data-hazard="weather"] .attribution-row         { order: 9; }

/* Inline SVG icon in a modal section header (replaces the old 📰 / 💡 emoji). */
.hdr-icon {
  width: 16px;
  height: 16px;
  vertical-align: -3px;
  margin-right: 7px;
  color: var(--text-secondary, #6b7280);
}

/* Road-closure hero map — a live map of the shut stretch at the top of the
   detail card. Fixed height so MapLibre has dimensions before it initialises. */
.road-hero-map {
  position: relative;
  height: 180px;
  border-radius: 12px;
  overflow: hidden;
  margin-bottom: 14px;
  background: var(--bg-deep, #EDE8DC);
  border: 1px solid var(--border, rgba(18, 21, 18, 0.12));
}
/* Promote the map canvas to its own compositor layer. Without this the WebGL
   canvas, created while the modal is still fading in, can render frames that the
   browser never composites to screen until an unrelated reflow (a known quirk,
   most visible on mobile). translateZ(0) forces a dedicated layer up front. */
.road-hero-map canvas { border-radius: 12px; transform: translateZ(0); backface-visibility: hidden; }
.road-hero-legend {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px;
  border-radius: 999px;
  background: rgba(20, 20, 20, 0.82);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.01em;
}
.road-hero-legend::before {
  content: "";
  width: 14px;
  height: 3px;
  border-radius: 2px;
  background: repeating-linear-gradient(90deg, #E8443B 0 4px, #1A1A1A 4px 6px);
}

/* Situation Summary — de-boxed. The lead reads as an official-wording quote (left
   rule); the structured fields ("What to do" / Impact / What's changed) sit below
   as labelled rows. No nested card (the old box-in-a-box). */
#alert-modal .description-card {
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
}
#modal-summary-content .ss-lead {
  border-left: 3px solid var(--text-muted, #6E7570);
  border-radius: 0;
  padding: 1px 0 1px 14px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-main);
}
#modal-summary-content .ss-fields {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 16px;
}
#modal-summary-content .ss-field {
  display: grid;
  grid-template-columns: minmax(72px, auto) 1fr;
  gap: 14px;
  align-items: start;
  border-top: 1px solid var(--border, rgba(18, 21, 18, 0.12));
  padding-top: 12px;
}
#modal-summary-content .ss-field:first-child { border-top: 0; padding-top: 0; }
#modal-summary-content .ss-flabel {
  font-size: 12px;
  font-weight: 800;
  line-height: 1.35;
  color: var(--text-secondary, #6E7570);
}
#modal-summary-content .ss-fbody {
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-muted, #6E7570);
}
#modal-summary-content .ss-field--action .ss-flabel { color: #B00610; }
#modal-summary-content .ss-field--action .ss-fbody { color: var(--text-main); }
/* "Note" boilerplate — quiet fine print, not a labelled row. */
#modal-summary-content .ss-note {
  margin: 14px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text-secondary, #6E7570);
}

/* ── Modal: type-aware detail cards ────────────────────────────────────────── */
/* Unify every metric number on the home's heavy tabular sans. The quake grid was
   JetBrains Mono and the telemetry dials were body Inter — neither matched the
   home stat board, which is the mismatch the eye catches. */
.modal-card .metric-card .val,
.modal-card .dial-value,
.modal-card .gauge-text {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.modal-card .metric-card .val,
.modal-card .dial-value { font-weight: 900; }
.modal-card .gauge-text { font-weight: 800; }

/* Modal heading + kicker share the home hero's type, not Fraunces serif /
   JetBrains Mono — so the detail card reads as the same product as the "ALL
   GOOD" header and the national-situation text. */
.modal-card #modal-headline {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.modal-card #modal-badge {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

/* Detail "Join the conversation" preview — latest message + join CTA. */
.modal-card .chat-entry { margin-bottom: 12px; }
.modal-card .chat-entry-preview {
  background: var(--bg-deep, #EDE8DC);
  border-radius: 12px;
  padding: 10px 12px;
  margin-bottom: 8px;
}
.modal-card .chat-entry-head {
  font-size: 10px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--text-secondary); margin-bottom: 5px;
}
.modal-card #modal-chat-here { color: var(--civic-ok, #0F6E56); }
.modal-card .chat-entry-msg {
  font-size: 13.5px; color: var(--text-main); line-height: 1.4;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* Felt-reports breakdown — plain-English headline + intensity spectrum that
   expands to the full named distribution (replaces the Roman-numeral pills). */
.modal-card #modal-felt-pills { display: block; }
.modal-card .felt-headline { font-size: 13.5px; line-height: 1.45; color: var(--text-main); margin: 0 0 2px; }
.modal-card .felt-headline b { font-weight: 800; }
.modal-card .felt-total { font-size: 11px; color: var(--text-secondary); margin: 0 0 9px; }
.modal-card .felt-spectrum { display: flex; height: 24px; border-radius: 7px; overflow: hidden; }
.modal-card .felt-spectrum > div { min-width: 2px; }
.modal-card .felt-scale { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-muted); margin-top: 4px; }
.modal-card .felt-expand {
  appearance: none; background: transparent; border: 0; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 600; color: var(--text-secondary);
  display: inline-flex; align-items: center; gap: 5px; padding: 8px 0 2px;
}
.modal-card .felt-expand .felt-chev { transition: transform 0.2s ease; }
.modal-card .felt-expand.open .felt-chev { transform: rotate(180deg); }
.modal-card .felt-dist { margin-top: 8px; }
.modal-card .felt-brow { display: grid; grid-template-columns: 92px 1fr auto; align-items: center; gap: 10px; margin-bottom: 7px; }
.modal-card .felt-bname { font-size: 13px; font-weight: 600; color: var(--text-main); }
.modal-card .felt-bname .m { display: block; font-size: 10px; font-weight: 500; color: var(--text-muted); }
.modal-card .felt-btrack { height: 15px; background: var(--bg-deep, #EDE8DC); border-radius: 5px; overflow: hidden; }
.modal-card .felt-bfill { height: 100%; border-radius: 5px; }
.modal-card .felt-bcount { font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums; min-width: 46px; text-align: right; color: var(--text-main); }

/* Estimate caption under the tsunami arrival timeline. */
.modal-card .rkc-estimate {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 9px;
  font-style: italic;
}

/* Text-valued metrics (tsunami threat, road status) — not the big numeric
   treatment; size to read as a word, not a number. */
.modal-card .metric-card .val.val-text {
  font-size: 15px;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.25;
}

/* "Local conditions" secondary telemetry reveals a normal tile grid. */
.modal-card #modal-secondary-content .telemetry-hud-grid { padding-top: 12px; }

/* When a collapsible is the first thing in the telemetry card (no primary tiles
   above it), drop its separator so there's no stray gap + rule above the toggle. */
.modal-card .local-breakdown-container.is-flush {
  margin-top: 0;
  border-top: none;
  padding-top: 0;
}

/* ── Type-aware hero cards (full visual port of the mockup) ─────────────────── */
.modal-card .rkc-hero { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.modal-card .rkc-sub {
  background: var(--bg-deep, #EDE8DC);
  border-radius: 14px;
  padding: 14px 16px;
}
.modal-card .rkc-leadrow { display: flex; align-items: center; gap: 14px; }
.modal-card .rkc-mmi-ring {
  width: 72px; height: 72px; border-radius: 50%;
  border: 5px solid var(--text-main); flex: none;
  display: flex; align-items: center; justify-content: center;
}
.modal-card .rkc-roman {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900; font-size: 28px; line-height: 1; font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
.modal-card .rkc-lead-label,
.modal-card .rkc-stat .k {
  font-size: 10px; font-weight: 700; letter-spacing: 0.5px; text-transform: uppercase;
  color: var(--text-secondary);
}
.modal-card .rkc-lead-title { font-size: 17px; font-weight: 600; color: var(--text-main); margin-top: 2px; }
.modal-card .rkc-lead-sub { font-size: 13px; color: var(--text-secondary); margin-top: 3px; }
/* Plain-language qualifier under the lead reading ("steady rain", "gale force"). */
.modal-card .rkc-lead-qual { font-size: 12.5px; font-weight: 600; color: var(--text-muted); margin-top: 4px; }

/* ── Living-weather ambience (ambience.js) ──────────────────────────────────
   The modal header becomes the "sky": condition tint + particle canvas +
   animated Meteocon. Type always beats atmosphere — the canvas is masked so
   particles run full-strength in the sky band and ghost out behind the
   headline, which also gets a paper halo. */
.modal-card .modal-header.has-ambience { overflow: hidden; }
.modal-card .modal-header.has-ambience .modal-title-group { position: relative; z-index: 1; }
.modal-card .modal-header.has-ambience .modal-title-group h3 {
  text-shadow: 0 1px 6px rgba(252, 248, 238, 0.85), 0 0 2px rgba(252, 248, 238, 0.7);
}
.modal-card .modal-header.has-ambience .modal-category-badge,
.modal-card .modal-header.has-ambience .modal-meta {
  text-shadow: 0 1px 4px rgba(252, 248, 238, 0.8);
}
.modal-card .ambience-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(170deg, #000 0%, #000 34%, rgba(0, 0, 0, 0.45) 62%, rgba(0, 0, 0, 0.14) 100%);
  mask-image: linear-gradient(170deg, #000 0%, #000 34%, rgba(0, 0, 0, 0.45) 62%, rgba(0, 0, 0, 0.14) 100%);
}
.modal-card .ambience-icon {
  position: absolute;
  top: 12px;
  right: 52px; /* clear of the close button */
  width: 60px;
  height: 60px;
  z-index: 1;
  pointer-events: none;
  filter: drop-shadow(0 2px 5px rgba(27, 32, 48, 0.16));
}
@media (max-width: 640px) {
  .modal-card .ambience-icon { width: 50px; height: 50px; right: 48px; top: 10px; }
}
.modal-card .rkc-num {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900; font-variant-numeric: tabular-nums; letter-spacing: -0.03em; line-height: 1;
  font-size: 34px;
}
.modal-card .rkc-num-sm {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900; font-variant-numeric: tabular-nums;
}
.modal-card .rkc-statrow {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--border); border: 1px solid var(--border);
  border-radius: 12px; overflow: hidden;
}
.modal-card .rkc-statrow.cols-2 { grid-template-columns: repeat(2, 1fr); }
.modal-card .rkc-stat { background: var(--civic-paper, #F4F1E8); padding: 10px 13px; }
.modal-card .rkc-stat .v {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900; font-size: 22px; font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
  color: var(--text-main); margin-top: 3px; display: block;
}
.modal-card .rkc-stat .v.v-text { font-size: 15px; letter-spacing: -0.01em; }
.modal-card .rkc-stat .u { font-size: 11px; font-weight: 600; color: var(--text-secondary); margin-left: 3px; }
/* Plain-language tier word under each stat number ("cold", "wet — floods form
   faster") — the number alone means nothing to a non-meteorologist. */
.modal-card .rkc-stat .hint { font-size: 10.5px; line-height: 1.3; color: var(--text-secondary); margin-top: 2px; }
.modal-card .rkc-scale { display: flex; gap: 4px; margin-top: 10px; }
.modal-card .rkc-scale span { flex: 1; height: 6px; border-radius: 3px; background: var(--border); }
.modal-card .rkc-scale-ends {
  display: flex; justify-content: space-between; font-size: 10px; color: var(--text-muted); margin-top: 5px;
}
.modal-card .rkc-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.modal-card .rkc-chip {
  font-size: 12px; padding: 5px 10px; border-radius: 8px;
  background: var(--bg-deep, #EDE8DC); border: 1px solid var(--border); color: var(--text-main);
}
.modal-card .rkc-lead-big { display: flex; align-items: baseline; gap: 10px; margin: 2px 0 2px; }

/* Volcano status rows (aviation / activity / hazards) */
.modal-card .rkc-srow {
  display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
  font-size: 13.5px; padding: 7px 0; border-top: 1px solid var(--border);
}
.modal-card .rkc-srow:first-of-type { border-top: none; padding-top: 4px; }
.modal-card .rkc-srow .k { color: var(--text-secondary); flex: none; }
.modal-card .rkc-srow .v { color: var(--text-main); text-align: right; }

/* Location-relative block (distance / nearest coast) — only rendered when the
   user's location is known; degrades by simply not appearing. */
.modal-card .rkc-loc {
  position: relative; border: 1px dashed rgba(18, 21, 18, 0.28); border-radius: 12px;
  padding: 11px 13px; margin: 0 0 10px; font-size: 14px; color: var(--text-main);
}
.modal-card .rkc-loc-badge {
  position: absolute; top: -8px; left: 11px; background: var(--civic-paper, #F4F1E8);
  font-size: 9px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase;
  color: var(--text-secondary); padding: 0 5px;
}

/* Tsunami arrival timeline */
.modal-card .rkc-tl { display: flex; align-items: flex-start; margin-top: 12px; }
.modal-card .rkc-stop { flex: 1; text-align: center; position: relative; min-width: 0; }
.modal-card .rkc-stop::before { content: ""; position: absolute; top: 5px; left: 0; right: 0; height: 2px; background: var(--border); }
.modal-card .rkc-stop:first-child::before { left: 50%; }
.modal-card .rkc-stop:last-child::before { right: 50%; }
.modal-card .rkc-dot { position: relative; width: 11px; height: 11px; border-radius: 50%; background: #9AA298; margin: 0 auto 6px; }
.modal-card .rkc-stop.near .rkc-dot { background: #1F66A8; box-shadow: 0 0 0 3px rgba(31, 102, 168, 0.28); }
.modal-card .rkc-t { font-size: 12px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--text-main); }
.modal-card .rkc-stop.near .rkc-t { color: #1F66A8; }
.modal-card .rkc-p { font-size: 10.5px; color: var(--text-secondary); margin-top: 1px; overflow: hidden; text-overflow: ellipsis; }
