/*
 * pheme.css — shared design-system foundation for the ~14 static pages in
 * pheme-backend/public/ (index, us, world, globe, world-simulate,
 * world-dashboard, ask, bills, why, feedback, and the four us/*.html
 * pages — explore, forecast, accuracy, live). dashboard.html still exists
 * as a thin redirect stub to /us/explore for old links/bookmarks; it isn't
 * a real page and doesn't load this file.
 *
 * Load this BEFORE any page-local <style> block:
 *   <link rel="stylesheet" href="/pheme.css" />
 * so page-specific rules keep the final say in the cascade (equal
 * specificity, later wins).
 *
 * Two intentional dark modes, not a bug:
 *   - reading mode (default :root) — the dashboard-family palette used by
 *     index / us / world-dashboard / ask / bills / why / feedback / the
 *     us/*.html pages.
 *   - immersive mode (`<html data-mode="immersive">`) — the deeper
 *     globe-family palette used by globe / world / world-simulate, meant
 *     to sit behind a WebGL canvas rather than a scrolling document.
 * Accent colors (orange/teal/gold/grey/purple) are identical in both
 * modes; only the neutrals (ink/panel/line/text/dim) and the glow tokens
 * shift. See MIGRATION-CONTRACT.md for the full per-page migration steps.
 *
 * Token provenance: the reading-mode block is the union of every :root
 * token declared across index/dashboard/world-dashboard/ask/bills.html
 * back when dashboard.html was still the full US data dashboard (now
 * split into the four us/*.html pages) — it had the fullest set (it alone
 * additionally declared --panel2/--grey/--purple/--sky/--green/--rose)
 * and was treated as the reference implementation wherever pages
 * disagreed. The immersive block is the union of globe.html + world.html's
 * :root blocks, with the glow blur pinned to globe.html's 18px (world.html's
 * local copy used 20px — that's the drift this file kills).
 */

/*
 * Design system v2 — "editorial wire service" direction (2026-07 redesign).
 * Was navy/gold/teal with 12-16px rounded-lg cards — competent-AI-dashboard,
 * not distinctively *Pheme* (a herald of rumor and report). New neutrals move
 * off blue-black toward a warm, newsprint-on-dark charcoal; corners sharpen
 * (radius scale below); a display serif joins the existing sans/mono for
 * headlines. Accent hex values (orange/teal/gold/grey/purple/sky/green/rose)
 * are UNCHANGED on purpose — they're load-bearing for reaction/data color
 * coding across globe.html/world-simulate.html/engine.mjs, not decoration,
 * so this pass doesn't touch them. Rollout of the new radius/spacing/display
 * tokens into per-page markup is staged across the redesign plan's later
 * phases; this file is the foundation every page already inherits from.
 */
:root {
  /* neutrals */
  --ink: #0c0b0a;
  --ink2: #131211;
  --panel: #1b1a18;
  --panel2: #232220;
  --line: #34322e;
  --line2: #47443e;
  --fog: #efe9de;
  --text: #efe9de;
  --dim: #a8a099;
  --dim2: #948f8c;

  /* accents — identical in both modes; unchanged, see note above */
  --orange: #f05530;
  --teal: #35b0a3;
  --gold: #e8b840;
  --grey: #8891a3;
  --purple: #9b8cff;
  --sky: #5bc0eb;
  --green: #7bd389;
  --rose: #ec6f9b;

  /* type */
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: "Fraunces", "Iowan Old Style", "Georgia", "Times New Roman", serif;

  /* radius scale — sharper than the old flat 8-16px everywhere. Pills
     (999px, tab underlines, dots) are untouched; those are functional
     shapes, not the "rounded-lg on everything" tell. */
  --radius-xs: 3px;
  --radius-sm: 5px;
  --radius-md: 7px;
  --radius-lg: 10px;

  /* spacing scale — for new/migrated components; existing per-page hardcoded
     padding isn't retrofitted wholesale, that's the page-by-page rollout. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
}

/* ---- immersive mode: globe.html / world.html opt in with data-mode="immersive" ---- */
html[data-mode="immersive"] {
  --ink: #070605;
  --ink2: #0c0b0a;
  --panel: #14120f;
  --panel2: #1a1815;
  --line: #2b2822;
  --line2: #3a362e;
  --fog: #f0ebe0;
  --text: #ddd6cb;
  --dim: #948d82;
  --dim2: #8c857b;

  /* canonical glow blur is 18px (globe.html) — world.html's local 20px copy loses */
  --glow-orange: 0 0 18px rgba(240, 85, 48, 0.35);
  --glow-teal: 0 0 18px rgba(53, 176, 163, 0.35);
  --glow-gold: 0 0 14px rgba(232, 184, 64, 0.3);
}

/* =========================================================================
   Skip link
   ========================================================================= */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--gold);
  color: var(--ink);
  padding: 10px 16px;
  border-radius: 0 0 8px 0;
  font: 700 13px var(--sans);
  text-decoration: none;
}
.skip-link:focus {
  left: 0;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* =========================================================================
   Global focus ring
   ========================================================================= */
:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Base link default. Every styled nav/button/card link overrides this with
   its own class (text-decoration: none, its own color, etc.), so this only
   ever actually renders on a plain, unclassed <a> dropped into body prose —
   which otherwise falls through to the browser's unstyled default blue
   against a dark background (found by axe: color-contrast + link-in-text-
   block on /why's plain-prose links). Underlined by default so it reads as
   a link even where color contrast against body text is close. */
a {
  color: var(--teal);
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 2px;
}
a:hover {
  color: var(--fog);
}

/* =========================================================================
   Site footer — canonical link row. index.html hand-writes its own footer
   markup on purpose (see the comment above its <footer> in index.html);
   every other non-immersive page ships an empty <footer></footer> that
   ui.mjs's renderSiteFooter() fills with SITE_FOOTER_HTML. Neither path
   styled the <a> tags, so they fell through to the browser's UA
   stylesheet: default link-blue, underlined, on every single page.
   ========================================================================= */
footer a {
  color: var(--dim);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition:
    color 0.12s,
    border-color 0.12s;
}
footer a:hover {
  color: var(--teal);
  border-color: var(--line2);
}

/* Utility for a link embedded in a run of prose (not a nav list): an
   underline so it's visually distinct from the surrounding text even where
   the two colors are close in value (axe's link-in-text-block rule). */
.link-u {
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 2px;
  opacity: 0.85;
}
.link-u:hover {
  opacity: 1;
}

/* =========================================================================
   Badge + status dot
   ========================================================================= */
.badge {
  /* was 9px on every page pre-migration; bumped to the 10px type floor */
  font: 700 10px var(--mono);
  color: var(--gold);
  border: 1px solid var(--line2);
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  text-transform: uppercase;
  letter-spacing: 1px;
}
.dot {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  background: var(--gold);
  box-shadow: 0 0 12px var(--gold);
}

/* =========================================================================
   .sitenav — the one pill nav every page shares
   ========================================================================= */
.sitenav {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-right: 10px;
}
.sitenav a {
  font: 500 12px var(--mono);
  color: var(--dim);
  text-decoration: none;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  letter-spacing: 0.2px;
  white-space: nowrap;
  transition:
    color 0.12s,
    border-color 0.12s;
}
.sitenav a:hover {
  color: var(--fog);
  border-color: var(--line);
}
/* current-page pill: markActiveNav() sets both aria-current="page" and
   class="on" (the site's existing toggle-state convention — see ui.mjs).
   .active is also styled here so pages mid-migration that still carry the
   legacy hand-written class keep working identically. */
.sitenav a.on,
.sitenav a.active,
.sitenav a[aria-current="page"] {
  color: var(--teal);
  border-color: var(--line2);
  background: var(--ink2);
}
.sitenav a:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}
@media (max-width: 720px) {
  .sitenav {
    order: 3;
    width: 100%;
    margin-right: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .sitenav::-webkit-scrollbar {
    display: none;
  }
  /* 44px touch target on mobile (flex item, so min-height applies); the row
     stays horizontally scrollable and desktop is untouched. */
  .sitenav a {
    min-height: 44px;
    display: flex;
    align-items: center;
  }
  .badge {
    order: 2;
    margin-left: auto;
  }
}

/* denser immersive variant — sits over a WebGL canvas instead of a page,
   so it gets its own translucent panel rather than relying on the host
   header for contrast, plus tighter padding to leave more canvas visible. */
html[data-mode="immersive"] .sitenav {
  gap: 2px;
  padding: 3px;
  background: rgba(17, 24, 38, 0.55);
  border: 1px solid rgba(30, 45, 71, 0.6);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
html[data-mode="immersive"] .sitenav a {
  padding: 4px 8px;
  font-size: 11px;
}
html[data-mode="immersive"] .sitenav a.on,
html[data-mode="immersive"] .sitenav a.active,
html[data-mode="immersive"] .sitenav a[aria-current="page"] {
  background: rgba(53, 176, 163, 0.12);
}

/* =========================================================================
   Site chrome v2 — intent-first two-row header
   ========================================================================= */
.site-header {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 14px 0 10px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 8px;
  /* Every page ships an empty <header id="site-header"></header> that JS
     fills in after initSiteChrome() runs, so without a floor the whole page
     jumps down once it renders (measured: a real 0.15-0.47 CLS hit, worst on
     pages with more content below the fold to shove around). Rendered
     height varies by page (54px on simple pages, 92px on pages that also
     render the scope-toggle row), so this floor doesn't erase the shift
     everywhere, but it removes the "starts at zero" worst case for free. */
  min-height: 54px;
}
.site-header--immersive {
  position: relative;
  z-index: 30;
  border-bottom: none;
  margin-bottom: 0;
  padding: 10px 12px 6px;
}
.site-header__row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.site-header__row--intent {
  padding-top: 8px;
  gap: 8px;
  align-items: flex-end;
}
.site-header .sp {
  flex: 1;
  min-width: 8px;
}
.site-header .brand {
  display: flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
  color: inherit;
  font: 800 19px var(--sans);
  letter-spacing: -0.4px;
}
.site-header .brand .u {
  color: var(--gold);
  font: 600 12px var(--mono);
}

.scopenav {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--ink2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 2px;
}
.scopenav a {
  font: 600 11px var(--mono);
  color: var(--dim);
  text-decoration: none;
  padding: 5px 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  transition:
    color 0.12s,
    background 0.12s;
}
.scopenav a:hover {
  color: var(--fog);
}
.scopenav a.on,
.scopenav a[aria-current="true"] {
  color: var(--fog);
  background: var(--panel);
}

.ask-cta {
  font: 700 12px var(--mono);
  color: var(--ink);
  background: var(--teal);
  text-decoration: none;
  padding: 7px 14px;
  border-radius: var(--radius-md);
  letter-spacing: 0.3px;
  white-space: nowrap;
  transition:
    filter 0.12s,
    transform 0.12s;
}
.ask-cta:hover,
.ask-cta:focus-visible {
  filter: brightness(1.08);
  transform: translateY(-1px);
}
/* keyboard users need a real ring, not just the brightness nudge */
.ask-cta:focus-visible {
  outline: 2px solid var(--orange);
  outline-offset: 2px;
}

/* =========================================================================
   .btn — canonical action button. bills.html relies on this (its Real Bills
   simulate / retry buttons carry class="btn" with no local rule), and the
   four us/*.html split pages used to each ship a byte-identical copy of this
   block inline. Defined here once so bills gets styled and the us pages can
   drop their duplicates. Pages that need a different look (globe.html,
   world-simulate.html) still define their own .btn in their <style>, which
   wins by cascade order.
   ========================================================================= */
.btn {
  border: 1px solid var(--line2);
  background: rgba(240, 85, 48, 0.12);
  color: var(--orange);
  border-radius: var(--radius-md);
  padding: 9px 14px;
  font: 800 12px var(--sans);
}
.btn:hover {
  background: rgba(240, 85, 48, 0.2);
}
.btn.ghost {
  background: transparent;
  color: var(--dim);
}

.intentnav {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-right: 0;
}
.intentnav a {
  font: 700 12px var(--mono);
  color: var(--dim);
  text-decoration: none;
  padding: 6px 12px 8px;
  border-radius: 0;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  letter-spacing: 0.2px;
}
.intentnav a:hover {
  color: var(--fog);
  border-color: var(--line2);
}
.intentnav a.on,
.intentnav a[aria-current="page"] {
  color: var(--teal);
  border-bottom-color: var(--teal);
  background: transparent;
}

.us-subnav {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: 4px;
  padding-left: 10px;
  border-left: 1px solid var(--line);
}
.us-subnav a {
  font: 600 11px var(--mono);
  color: var(--dim2);
  text-decoration: none;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
}
.us-subnav a:hover {
  color: var(--fog);
}
.us-subnav a.on,
.us-subnav a[aria-current="page"] {
  color: var(--gold);
  background: var(--ink2);
}

.mobile-ask-bar {
  display: none;
}
/* Mobile nav toggle — hidden entirely above the collapse breakpoint, where
   .scopenav and .site-header__row--intent are just always-visible flex rows
   like before. See initMobileNavToggle() in ui.mjs. */
.nav-toggle {
  display: none;
}
@media (max-width: 720px) {
  .site-header__row--top .ask-cta {
    display: none;
  }
  .scopenav a {
    padding: 5px 8px;
    font-size: 10px;
    min-height: 44px;
    display: flex;
    align-items: center;
  }
  .intentnav {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .intentnav::-webkit-scrollbar {
    display: none;
  }
  .us-subnav {
    width: 100%;
    margin-left: 0;
    padding-left: 0;
    border-left: none;
    border-top: 1px solid var(--line);
    padding-top: 6px;
    overflow-x: auto;
    /* Phase 8: DOM order is intentnav-then-us-subnav, so tab order already hits
       the intent tabs first — give this a higher order than .sitenav's order:3
       so visual order agrees with tab order instead of contradicting it. */
    order: 4;
  }
  /* Collapse: was 3 stacked full-width rows (brand, scope pills, intent
     tabs) before any content. Now brand + badge + one toggle button on
     row 1; scope pills and intent tabs both hide until tapped open, so the
     default mobile header is one row tall instead of three. */
  .nav-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    font: 700 11px var(--mono);
    color: var(--dim);
    background: var(--ink2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 7px 10px;
    min-height: 32px;
    order: 1;
  }
  .nav-toggle-bars,
  .nav-toggle-bars::before,
  .nav-toggle-bars::after {
    content: "";
    display: block;
    width: 13px;
    height: 1.5px;
    background: currentColor;
  }
  .nav-toggle-bars {
    position: relative;
  }
  .nav-toggle-bars::before {
    position: absolute;
    top: -4px;
  }
  .nav-toggle-bars::after {
    position: absolute;
    top: 4px;
  }
  .nav-toggle[aria-expanded="true"] {
    color: var(--fog);
    border-color: var(--line2);
  }
  .site-header__row--top .scopenav {
    order: 2;
    display: none;
    width: 100%;
  }
  .site-header__row--intent {
    display: none;
  }
  .site-header.nav-open .site-header__row--top .scopenav,
  .site-header.nav-open .site-header__row--intent {
    display: flex;
  }
  .site-header.nav-open .site-header__row--top {
    padding-bottom: 2px;
  }
  .mobile-ask-bar {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 35;
    height: 48px;
    background: var(--teal);
    color: var(--ink);
    font: 800 13px var(--mono);
    text-decoration: none;
    letter-spacing: 0.3px;
    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.35);
  }
  body {
    padding-bottom: 56px !important;
  }
}

html[data-mode="immersive"] .site-header--immersive {
  background: rgba(8, 12, 20, 0.72);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 0 0 12px 12px;
}
html[data-mode="immersive"] .scopenav {
  background: rgba(17, 24, 38, 0.55);
  border-color: rgba(30, 45, 71, 0.6);
}
html[data-mode="immersive"] .intentnav a.on,
html[data-mode="immersive"] .intentnav a[aria-current="page"] {
  border-bottom-color: var(--teal);
}

/* Onboarding banner — a dismissible, non-blocking strip docked to the
   viewport bottom (was a full-screen modal; see ui.mjs initOnboardingBanner).
   Sits above the mobile Ask bar's z-index so both can coexist briefly. */
.onboard-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 70;
  background: var(--panel);
  border-top: 1px solid var(--line2);
  box-shadow: 0 -12px 30px rgba(0, 0, 0, 0.35);
}
.onboard-banner-row {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 18px;
  padding: 10px 16px;
}
.onboard-banner-lede {
  font: 500 13px var(--sans);
  color: var(--dim);
  flex: 1 1 220px;
}
.onboard-banner-lede b {
  color: var(--fog);
}
.onboard-banner-links {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}
.onboard-banner-links a {
  font: 700 12px var(--mono);
  color: var(--teal);
  text-decoration: none;
  white-space: nowrap;
}
.onboard-banner-links a:hover,
.onboard-banner-links a:focus-visible {
  color: var(--gold);
}
.onboard-banner-dismiss {
  font: 700 14px var(--sans);
  color: var(--dim);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  width: 28px;
  height: 28px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
}
.onboard-banner-dismiss:hover,
.onboard-banner-dismiss:focus-visible {
  color: var(--fog);
  border-color: var(--line2);
}
/* Guided-tour strip: opt-in via ?tour=1, only on reading-mode pages */
.tour-strip {
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 16px;
  background: var(--panel);
  border-bottom: 1px solid var(--line2);
  padding: 9px 16px;
  margin: 0 0 8px;
  font: 600 12px var(--mono);
  color: var(--dim);
}
.tour-strip b {
  color: var(--gold);
}
.tour-strip a {
  color: var(--teal);
  text-decoration: none;
}
.tour-strip a:hover,
.tour-strip a:focus-visible {
  color: var(--gold);
}
.tour-strip-end {
  margin-left: auto;
  font: 600 11px var(--mono);
  color: var(--dim2);
  background: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 4px 9px;
  cursor: pointer;
}
.tour-strip-end:hover,
.tour-strip-end:focus-visible {
  color: var(--fog);
  border-color: var(--line2);
}

@media (max-width: 560px) {
  .onboard-banner-row {
    padding: 10px 12px 12px;
  }
  .onboard-banner-lede {
    flex-basis: 100%;
  }
  /* lift above the mobile Ask bar (also fixed-bottom) so they don't stack —
     :has() fallback for this lives with the other one, near #feedback-fab. */
  body:has(#mobile-ask-bar) .onboard-banner {
    bottom: 48px;
  }
}

/* Hub cards (us.html / world hub) */
.hub-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 12px;
  margin: 20px 0;
}
.hub-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  background: var(--panel);
  transition:
    border-color 0.12s,
    transform 0.12s;
}
.hub-card:hover {
  border-color: var(--line2);
  transform: translateY(-2px);
}
.hub-card b {
  font: 700 14px var(--sans);
}
.hub-card span {
  font: 500 13px var(--sans);
  color: var(--dim);
  line-height: 1.45;
}
.hub-card .go {
  margin-top: auto;
  font: 700 11px var(--mono);
  color: var(--teal);
}

/* Intent chooser on landing */
.intent-chooser {
  margin: 28px 0;
}
.intent-chooser h2 {
  font: 600 21px var(--font-display);
  margin: 0 0 12px;
  letter-spacing: -0.2px;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 16px;
}
.tour-cta {
  font: 700 12px var(--mono);
  color: var(--teal);
  text-decoration: none;
}
.tour-cta:hover,
.tour-cta:focus-visible {
  color: var(--gold);
}
.intent-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px;
}
.intent-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  cursor: pointer;
  text-align: left;
  color: inherit;
  font: inherit;
  transition:
    border-color 0.12s,
    background 0.12s;
}
.intent-card:hover,
.intent-card.on {
  border-color: var(--teal);
  background: var(--ink2);
}
.intent-card b {
  font: 700 13px var(--mono);
  color: var(--teal);
}
.intent-card span {
  font: 500 13px var(--sans);
  color: var(--dim);
}
/* "See it checked against real polls" homepage demo */
.try-now {
  margin: 28px 0;
}
.try-now h2 {
  font: 600 21px var(--font-display);
  margin: 0 0 6px;
  letter-spacing: -0.2px;
}
.try-now .sub {
  font: 500 13px var(--sans);
  color: var(--dim);
  margin: 0 0 14px;
  max-width: 620px;
  line-height: 1.5;
}
.try-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}
.try-chip {
  font: 600 12px var(--mono);
  color: var(--dim);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 14px;
  cursor: pointer;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition:
    border-color 0.12s,
    color 0.12s;
}
.try-chip:hover,
.try-chip.on {
  border-color: var(--teal);
  color: var(--fog);
}
.try-result {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
  padding: 18px 20px;
}
.try-result .q {
  font: 600 15px var(--sans);
  color: var(--fog);
  margin: 0 0 12px;
}
.try-bar-row {
  display: grid;
  grid-template-columns: 78px 1fr 46px;
  align-items: center;
  gap: 8px;
  font: 600 12px var(--mono);
  margin-bottom: 8px;
}
.try-bar-row .lab {
  color: var(--dim2);
}
.try-bar-track {
  height: 10px;
  border-radius: 5px;
  background: var(--ink2);
  overflow: hidden;
  position: relative;
}
.try-bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  border-radius: 5px;
}
.try-result .err {
  font: 600 12px var(--mono);
  color: var(--dim2);
}
.try-result .src {
  font: 500 11px var(--mono);
  color: var(--dim2);
  margin-top: 10px;
}
.try-result .src a {
  color: inherit;
}
.try-result .cta {
  display: inline-block;
  margin-top: 12px;
  font: 700 13px var(--mono);
  color: var(--teal);
}

.geo-picker {
  display: flex;
  gap: 8px;
  margin: 16px 0 8px;
  flex-wrap: wrap;
}
.geo-picker button {
  font: 700 12px var(--mono);
  color: var(--dim);
  background: var(--ink2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 8px 16px;
  cursor: pointer;
}
.geo-picker button.on {
  color: var(--fog);
  border-color: var(--teal);
  background: color-mix(in srgb, var(--teal) 12%, var(--ink2));
}
.intent-dest {
  margin-top: 12px;
}
.intent-dest a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font: 700 13px var(--mono);
  color: var(--teal);
  text-decoration: none;
  padding: 10px 16px;
  border: 1px solid var(--line2);
  border-radius: var(--radius-md);
  background: var(--ink2);
}
.intent-dest a:hover {
  border-color: var(--teal);
}

/* =========================================================================
   KPI modal — canonical implementation from dashboard.html, shared by
   any page that opens a stat card into a detail overlay (dashboard,
   world-dashboard today; available to any future page for free).
   ========================================================================= */
.kpi-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(14, 17, 23, 0.7);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}
.kpi-backdrop.show {
  opacity: 1;
  pointer-events: auto;
}
.kpi-modal {
  background: rgba(23, 29, 43, 0.85);
  border: 1px solid var(--line2);
  border-radius: var(--radius-lg);
  padding: 24px;
  width: 90%;
  max-width: 580px;
  box-shadow:
    0 20px 40px rgba(0, 0, 0, 0.5),
    inset 0 1px 0 rgba(255, 255, 255, 0.05);
  transform: translateY(20px);
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
  color: var(--text);
}
.kpi-backdrop.show .kpi-modal {
  transform: translateY(0);
}
.kpi-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--line);
  padding-bottom: 12px;
}
.kpi-head h2 {
  margin: 0;
  font: 800 20px var(--sans);
  color: var(--fog);
}
.kpi-head .close-btn {
  background: transparent;
  border: none;
  color: var(--dim);
  font-size: 20px;
  cursor: pointer;
  transition: color 0.15s;
  padding: 0 4px;
  line-height: 1;
}
.kpi-head .close-btn:hover {
  color: var(--orange);
}
.kpi-body {
  font-size: 13px;
  line-height: 1.6;
  color: var(--dim);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.kpi-body b {
  color: var(--fog);
}
.kpi-body code {
  font-family: var(--mono);
  color: var(--gold);
  background: rgba(232, 184, 64, 0.08);
  padding: 2px 5px;
  border-radius: 4px;
}
.kpi-body .formula {
  background: var(--ink2);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  font-family: var(--mono);
  color: var(--purple);
  text-align: center;
  margin: 6px 0;
}
.kpi-links {
  margin-top: 18px;
  border-top: 1px solid var(--line);
  padding-top: 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.kpi-links a {
  font: 700 12px var(--mono);
  color: var(--orange);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: color 0.15s;
}
.kpi-links a:hover {
  color: var(--fog);
  text-decoration: underline;
}
@media (max-width: 560px) {
  .kpi-head .close-btn {
    padding: 6px 10px;
  }
}

/* =========================================================================
   Sim-menu redesign (Phase 2) — the Featured grid shown by default in the
   "Run a simulation" modal, plus a `.browse-toggle`-style reuse of the
   existing `.sim-advanced` details/summary pattern for the "Browse all
   scenarios" drawer that wraps the full category-tab picker. New
   component: neither globe.html nor world.html shipped a Featured view
   before this, so there's no prior "canonical source" to reconcile.
   ========================================================================= */
.featured-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
  margin-bottom: 10px;
}
.featured-card {
  cursor: pointer;
  display: flex;
  align-items: flex-start;
  gap: 7px;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--ink2);
  color: var(--dim);
  padding: 8px 9px;
  font: 500 11px var(--sans);
  line-height: 1.3;
  transition:
    color 0.1s,
    border-color 0.1s,
    background 0.1s;
}
.featured-card:hover {
  color: var(--fog);
  border-color: var(--line2);
  background: var(--panel2);
}
.featured-card.sel {
  color: var(--fog);
  background: color-mix(in srgb, var(--orange) 16%, transparent);
  border-color: var(--orange);
}
/* First-run clarity (Phase 6): one obvious place to start among 16 cards. */
.featured-card.featured-start {
  position: relative;
  border-color: color-mix(in srgb, var(--teal) 55%, var(--line));
}
.featured-card.featured-start::after {
  content: "Try this";
  position: absolute;
  top: -7px;
  right: 8px;
  font: 700 9px var(--mono);
  letter-spacing: 0.3px;
  color: var(--ink);
  background: var(--teal);
  padding: 1px 6px;
  border-radius: 999px;
}
.featured-card.featured-start.sel::after {
  display: none;
}
.featured-card .icon {
  flex-shrink: 0;
  font-size: 14px;
  line-height: 1.3;
}
.featured-card .ftxt {
  min-width: 0;
}
@media (max-width: 560px) {
  .featured-grid {
    grid-template-columns: 1fr;
  }
  /* Phase 6: 44px minimum tap target on touch-sized viewports. */
  .featured-card {
    min-height: 44px;
    padding: 11px 12px;
  }
}

/* =========================================================================
   Event bubbles (.pop / .chat-bubble) — the floating reaction-popup and
   chatter-voice cards shown over the map during a running simulation.
   Canonical source: world.html's versions (world.html:1266-1365 for .pop,
   :1790-1905 for #chatter/.chat-bubble) — kept over globe.html's copies
   because world.html is the superset: it adds the pop-title icon/tag row
   (.pop-dot, "em" tag), pop-meta line, and — for chat bubbles — the
   cb-head/cb-glyph/cb-who/cb-tag structure plus chat-resident/official/
   news/creator type modifiers that globe.html's simpler bubbles lack
   entirely. Both pages' local <style> blocks still declare their own
   copies today (globe.html's is the plainer one); a later phase removes
   those local blocks once each page is wired to bubbles.mjs, which is
   what actually emits this markup shape for both scopes.
   ========================================================================= */
.pop {
  position: absolute;
  transform: translate(-50%, -132%);
  max-width: 248px;
  min-width: 150px;
  padding: 9px 12px 8px 13px;
  border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--cc, var(--line2)) 55%, transparent);
  border-left: 3px solid var(--cc, var(--line2));
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--cc, #0a182e) 14%, rgba(9, 18, 34, 0.93)),
    rgba(9, 18, 34, 0.95)
  );
  color: var(--fog, #e6f1ff);
  box-shadow:
    0 6px 22px rgba(0, 0, 0, 0.45),
    0 0 16px color-mix(in srgb, var(--cc, var(--teal)) 22%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 38;
  animation: popIn 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes popIn {
  from {
    opacity: 0;
    transform: translate(-50%, -120%) scale(0.92);
  }
  to {
    opacity: 1;
    transform: translate(-50%, -132%) scale(1);
  }
}
.pop-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font: 700 11px var(--sans);
  color: var(--fog);
  margin-bottom: 3px;
}
.pop-title em {
  margin-left: auto;
  font: 700 10px var(--mono);
  font-style: normal;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--cc, var(--teal));
  background: color-mix(in srgb, var(--cc, var(--teal)) 16%, transparent);
  padding: 2px 6px;
  border-radius: 999px;
  white-space: nowrap;
}
.pop-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--cc, var(--teal));
  box-shadow: 0 0 8px var(--cc, var(--teal));
  flex-shrink: 0;
}
.pop-body {
  display: block;
  font: 500 11.5px var(--sans);
  line-height: 1.4;
  color: #cdd9ec;
}
.pop-meta {
  display: block;
  margin-top: 5px;
  font: 600 10px var(--mono);
  letter-spacing: 0.3px;
  color: var(--dim2);
  text-transform: uppercase;
}
/* anchor: a dot at the map point with a thin line up to the card. The card
   is translateX(-50%), so the map point is its horizontal center. */
.pop::after {
  content: "";
  position: absolute;
  bottom: -16px;
  left: 50%;
  transform: translateX(-50%);
  width: 1.5px;
  height: 16px;
  background: linear-gradient(to top, transparent, var(--cc, var(--teal)));
  opacity: 0.85;
}
.pop::before {
  content: "";
  position: absolute;
  bottom: -22px;
  left: 50%;
  transform: translateX(-50%);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--cc, var(--teal));
  box-shadow: 0 0 9px var(--cc, var(--teal));
  opacity: 0.95;
}

#chatter {
  position: fixed;
  inset: 0;
  z-index: 35;
  pointer-events: none;
  overflow: hidden;
  transition: opacity 0.45s ease;
}
/* During a birdseye replay, clear the floating voices so the top-down
   impact + spread reads unobstructed. */
body.birdseye-view #chatter {
  opacity: 0;
}
.chat-bubble {
  position: absolute;
  max-width: 264px;
  min-width: 170px;
  transform: translate(-50%, 0);
  border-radius: var(--radius-md);
  border: 1px solid color-mix(in srgb, var(--cc, #35b0a3) 50%, transparent);
  border-left: 3px solid var(--cc, #35b0a3);
  padding: 9px 13px 10px;
  background: linear-gradient(
    180deg,
    color-mix(in srgb, var(--cc, #0a182e) 12%, rgba(10, 20, 38, 0.92)),
    rgba(9, 17, 33, 0.95)
  );
  color: var(--fog, #e6f1ff);
  box-shadow:
    0 6px 22px rgba(0, 0, 0, 0.45),
    0 0 16px color-mix(in srgb, var(--cc, #35b0a3) 24%, transparent);
  animation: chatPop 7.2s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
/* clickable bubbles opt back into pointer events — #chatter itself stays
   pointer-events:none so empty space between bubbles doesn't block the
   map/globe underneath. */
.chat-bubble.chat-clickable {
  pointer-events: auto;
  cursor: pointer;
  transition: box-shadow 0.15s;
}
.chat-bubble.chat-clickable:hover,
.chat-bubble.chat-clickable:focus-visible {
  box-shadow:
    0 6px 22px rgba(0, 0, 0, 0.45),
    0 0 22px color-mix(in srgb, var(--cc, #35b0a3) 45%, transparent);
  outline: none;
}
.cb-head {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 5px;
}
.cb-glyph {
  font-size: 12px;
  line-height: 1;
  flex-shrink: 0;
  font-style: normal;
  filter: drop-shadow(0 0 4px var(--cc, #35b0a3));
}
.cb-who {
  font: 700 11px var(--sans);
  color: var(--fog);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cb-tag {
  margin-left: auto;
  font: 700 10px var(--mono);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--cc, #35b0a3);
  background: color-mix(in srgb, var(--cc, #35b0a3) 16%, transparent);
  padding: 2px 6px;
  border-radius: 999px;
  white-space: nowrap;
  flex-shrink: 0;
}
.cb-body {
  display: block;
  font: 500 12.5px var(--sans);
  line-height: 1.45;
  color: #d3deef;
}
/* type cues that read independent of the sentiment color */
.chat-official .cb-glyph {
  filter: drop-shadow(0 0 4px #cdb060);
}
.chat-news {
  border-left-color: #e8b840;
}
.chat-news .cb-tag {
  color: #1a1410;
  background: #e8b840;
}
.chat-creator {
  border-style: dashed;
}
.chat-bubble::after {
  content: "";
  position: absolute;
  bottom: -22px;
  left: 50%;
  width: 1.5px;
  height: 22px;
  background: linear-gradient(to top, transparent, var(--cc, #35b0a3));
  box-shadow: 0 0 6px var(--cc, #35b0a3);
  opacity: 0.85;
}
.chat-bubble .ben {
  display: block;
  margin-top: 4px;
  font: italic 400 11px var(--sans);
  opacity: 0.66;
}
@keyframes chatPop {
  0% {
    opacity: 0;
    transform: translate(-50%, 22px) scale(0.88);
  }
  8% {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
  }
  72% {
    opacity: 1;
    transform: translate(-50%, -52px) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -84px) scale(0.96);
  }
}

/* =========================================================================
   News toolbar — refresh + topic/state filter chips, shared by any page
   with a headline feed (dashboard.html today; world-dashboard.html as a
   stretch goal). New component: no page shipped this before, so there's
   no "canonical source" to reconcile — this is the one true version.
   ========================================================================= */
.news-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 12px;
}
.news-toolbar .news-updated {
  font: 600 11px var(--mono);
  color: var(--dim2);
  margin-right: auto;
}
.news-toolbar .news-refresh {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font: 700 11px var(--mono);
  color: var(--teal);
  background: color-mix(in srgb, var(--teal) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--teal) 35%, transparent);
  border-radius: 999px;
  padding: 6px 12px;
  cursor: pointer;
  transition:
    background 0.15s,
    opacity 0.15s;
}
.news-toolbar .news-refresh:hover:not(:disabled) {
  background: color-mix(in srgb, var(--teal) 18%, transparent);
}
.news-toolbar .news-refresh:disabled {
  opacity: 0.55;
  cursor: default;
}
.news-toolbar .news-refresh .spin {
  display: inline-block;
  animation: newsSpin 0.8s linear infinite;
}
@keyframes newsSpin {
  to {
    transform: rotate(360deg);
  }
}
.news-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  width: 100%;
}
.news-chip {
  font: 600 11px var(--mono);
  color: var(--dim);
  background: var(--ink2);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 5px 11px;
  cursor: pointer;
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.news-chip:hover {
  color: var(--fog);
  border-color: var(--line2);
}
.news-chip.on {
  color: var(--ink);
  background: var(--gold);
  border-color: var(--gold);
}
.news-chip select {
  font: inherit;
  color: inherit;
  background: transparent;
  border: none;
}
.news-empty,
.news-error {
  font: 500 12px var(--sans);
  color: var(--dim);
  padding: 18px 4px;
  text-align: center;
}
.news-error {
  color: var(--orange);
}
.news-count {
  font: 700 10px var(--mono);
  color: var(--dim2);
  letter-spacing: 0.4px;
}
@media (max-width: 560px) {
  /* Phase 6: 44px minimum tap target on touch-sized viewports. */
  .news-toolbar .news-refresh,
  .news-chip {
    min-height: 44px;
    padding-top: 13px;
    padding-bottom: 13px;
  }
}

/* =========================================================================
   Reduced motion — scoped to the shared components above. Page-specific
   animations (canvas FX, etc.) are each page's own responsibility and get
   their own reduced-motion rules.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  .sitenav a,
  .kpi-backdrop,
  .kpi-modal,
  .kpi-head .close-btn,
  .kpi-links a,
  .pop,
  .chat-bubble,
  .news-toolbar .news-refresh .spin,
  #feedback-fab {
    animation: none !important;
    transition: none !important;
  }
}

/* =========================================================================
   #feedback-fab — the floating "report a bug / suggest something" button.
   Bottom-left, deliberately: the two globes already use the bottom-right
   corner for the cinematic-mode toggle.
   ========================================================================= */
#feedback-fab {
  position: fixed;
  left: 16px;
  bottom: 16px;
  z-index: 40;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--panel, #171d2b);
  border: 1px solid var(--line2, #39455c);
  text-decoration: none;
  font-size: 18px;
  line-height: 1;
  box-shadow: 0 8px 20px -8px rgba(0, 0, 0, 0.55);
  transition:
    transform 0.15s,
    border-color 0.15s,
    box-shadow 0.15s;
}
#feedback-fab:hover,
#feedback-fab:focus-visible {
  transform: translateY(-2px);
  border-color: var(--gold, #e8b840);
  box-shadow: 0 12px 26px -10px rgba(0, 0, 0, 0.65);
  outline: none;
}
@media (max-width: 720px) {
  #feedback-fab {
    width: 40px;
    height: 40px;
    font-size: 16px;
    left: 12px;
    bottom: 12px;
  }
  /* .mobile-ask-bar is a fixed, full-width 48px bar at the very bottom on
     pages that have one (see initMobileAskBar in ui.mjs) — lift the FAB
     above it so the two don't visually stack on top of each other. */
  body:has(#mobile-ask-bar) #feedback-fab {
    bottom: 60px;
  }
  /* Fallback for browsers without :has() (older Safari/Firefox): they can't
     detect the ask bar from CSS, so lift the FAB unconditionally at this
     breakpoint — a slightly higher FAB is harmless; an overlap isn't. */
  @supports not (selector(:has(*))) {
    #feedback-fab {
      bottom: 60px;
    }
    .onboard-banner {
      bottom: 48px;
    }
  }
}

/* =========================================================================
   .help / #help-pop — the ⓘ help affordance + its shared tooltip popover.
   Wired by initHelpBubbles() in ui.mjs. This block used to be copy-pasted
   into the inline <style> of nine pages (bills, ask, world-dashboard, globe,
   world-simulate, and the four us/*); it's identical everywhere so it lives
   here now. Page-local overrides (e.g. .metric-cell .help on the us
   dashboard) still sit in that page's own stylesheet, later in the cascade.
   ========================================================================= */
.help {
  all: unset;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  min-height: 28px;
  width: 28px;
  height: 28px;
  margin-left: 2px;
  border-radius: 50%;
  border: 1px solid var(--line2);
  color: var(--dim2);
  font: 700 11px var(--sans);
  cursor: pointer;
  vertical-align: middle;
  transition:
    color 0.12s,
    border-color 0.12s,
    background 0.12s;
}
.help:hover,
.help:focus-visible {
  color: var(--ink);
  background: var(--gold);
  border-color: var(--gold);
  outline: none;
}
@media (max-width: 720px) {
  /* Phase 8: 44px minimum tap target (was 28px, no mobile override before) */
  .help {
    min-width: 44px;
    min-height: 44px;
    width: 44px;
    height: 44px;
  }
}
#help-pop {
  position: fixed;
  z-index: 120;
  width: 240px;
  background: var(--ink2);
  border: 1px solid var(--gold);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  font: 500 12px var(--sans);
  color: var(--fog);
  line-height: 1.45;
  box-shadow: 0 8px 26px -10px rgba(0, 0, 0, 0.8);
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition:
    opacity 0.14s,
    transform 0.14s;
}
#help-pop.show {
  opacity: 1;
  transform: translateY(0);
}

/* feed status panel (/why#data, /setup) */
.feed-status-panel {
  margin: 18px 0 8px;
  padding: 16px 18px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  font-size: 13px;
  color: var(--dim);
}
.feed-status-head {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 16px;
  margin-bottom: 12px;
  font: 600 11px var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--dim2);
}
.feed-macro {
  color: var(--gold);
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
}
.feed-status-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 8px;
}
.feed-status-list li {
  padding: 8px 10px;
  border-radius: var(--radius-md);
  background: var(--ink2);
  border: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.45;
}
/* Muted via explicit vetted-contrast colors, not opacity — opacity dims text
   and background together, which pushes small mono text below WCAG AA
   (axe: color-contrast, needs 4.5:1) while still reading as "off" against
   --ink2 at full brightness. */
.feed-status-list li.off,
.feed-status-list li.off b {
  color: var(--dim);
}
.feed-state {
  float: right;
  font: 600 10px var(--mono);
  color: var(--dim);
  text-transform: uppercase;
}
.feed-status-list li.on .feed-state {
  color: var(--teal);
}
.feed-status-foot {
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--dim2);
}
.feed-status-foot a {
  color: var(--teal);
}
