/* Election Night 2026: the TDI hub's light and dark themes (tokens vendored in css/tdi/). Blue = Democratic, red = Republican, other parties neutral, amber/gold = projected or final. */

:root {
  /* Surfaces, text, borders, spacing, radii, type scale and the hub's identity hues (--gold, --pro, --anti, --blue, --tweets, --orgs...)
     come from css/tdi/tokens.css, vendored byte-for-byte from the TDI hub. This block only adds what the dashboard owns. */

  /* ---- party (the election convention; never used for chrome, never for status) ---------------------------------------------
     TEXT and lines on the page flip per theme (>= 4.5:1 on bg, panel, panel2, sunken). FILLS (map, bars, badges, needle ring) are one
     value in both themes: every one is >= 3.4:1 on all four grounds in both themes, so nothing repaints when the theme changes.
     Each colour is declared twice like the hub does: a plain dark hex (old browsers, and js/core/colors.selftest.mjs reads it), then light-dark(). */
  --d: #5b9bff;
  --d: light-dark(#1958cc, #5b9bff);
  --r: #ff6b61;
  --r: light-dark(#b1251b, #ff6b61);
  --d-fill: #2b6fe0; /* generated from js/core/colors.js (the selftest fails if they drift) */
  --r-fill: #d9453b;
  --r-badge: #c93d33; /* behind white text: 5.0:1 (--r-fill is 4.3:1) */
  --on-party: #ffffff; /* text on --d-fill / --r-badge / --grey: 4.7 / 5.0 / 4.6 */
  --mid: #6a6e76;
  --tossup: #857ba3; /* was #8e8aa8: 2.7:1 on the light panel; this is >= 3.2:1 on every ground, both themes */
  --grey: #6b7686; /* every third party (I, L, G, O) is neutral: no teal (hub --pro) or gold (hub --gold) in a party badge */
  --ind: #6b7686;
  /* the stepped / rating ramps (generated from colors.js; unchanged) */
  --rate-d1: #646e95;
  --rate-r1: #926664;
  --rate-d2: #5a6eb0;
  --rate-r2: #ad5e56;
  --rate-d3: #4a6ec8;
  --rate-r3: #c45348;
  --rate-d4: #2b6fe0;
  --rate-r4: #d9453b;
  --bin-d1: #646e95;
  --bin-r1: #926664;
  --bin-d2: #5c6eac;
  --bin-r2: #aa5f58;
  --bin-d3: #516ebf;
  --bin-r3: #bc574d;
  --bin-d4: #416fd1;
  --bin-r4: #cc4f43;
  --bin-d5: #2b6fe0;
  --bin-r5: #d9453b;

  /* ---- chrome: the hub's semantics, not the party's ----------------------------------------------------------------------- */
  --link: var(--blue); /* links were party blue (--d): every link read as "Democratic". The hub's steel blue, underlined in running text */
  --accent: var(--gold); /* amber TEXT / lines / borders: "projected", "final", the key race ring */
  --accent-fill: var(--gold-fill); /* amber as a FILL (needs ink on it): was var(--accent) on backgrounds */
  --accent-ink: var(--on-gold);
  --ok: var(--pro);
  --warn: var(--gold);
  --bad: var(--anti-text); /* failure red is the hub's, always with an icon + word (never a bare red dot beside a party colour) */
  --focus: var(--gold); /* the hub's ring: 2px gold, 2px offset */

  /* ---- map and charts ------------------------------------------------------------------------------------------------------ */
  --map-ink: #ffffff;
  --map-ink: light-dark(#16181b, #ffffff); /* selection / hover outline; >= 3.5:1 on every fill, both themes */
  --proj-edge: #f4b740;
  --proj-edge: light-dark(#16181b, #f4b740); /* projected / final outline: amber on dark, ink on light (no amber reaches 3:1 on the light ground AND the fills) */
  --map-bg: var(--sunken);
  --map-flat: var(--border); /* a state/county with no race: recessive but shaped */
  --map-edge: var(--panel); /* hairline between areas */
  --nodata-base: var(--panel2); /* the "no votes yet" hatch: was #1b232d / #4b5666 in js and css */
  --nodata-line: var(--control-border);
  --tp-forecast: var(--spend);
  --tp-markets: var(--orgs);
  --tp-live: var(--text);
  --shadow: 0 8px 24px rgba(0, 0, 0, 0.35); /* the hub's popover shadow */

  /* balance-of-power segments: "called" is the full party fill; likely / lean tint it toward the ground (and lean keeps its hatch) */
  --seg-d-called: #2b6fe0;
  --seg-r-called: #d9453b;
  --seg-d-fixed: #3a5a8c;
  --seg-d-fixed: light-dark(color-mix(in srgb, #2b6fe0 55%, #6a6e76), #3a5a8c);
  --seg-d-likely: #28559d;
  --seg-d-likely: light-dark(color-mix(in srgb, #2b6fe0 80%, var(--panel2)), #28559d);
  --seg-d-lean: #264473;
  --seg-d-lean: light-dark(color-mix(in srgb, #2b6fe0 62%, var(--panel2)), #264473);
  --seg-r-fixed: #8a4945;
  --seg-r-fixed: light-dark(color-mix(in srgb, #d9453b 55%, #6a6e76), #8a4945);
  --seg-r-likely: #903b3a;
  --seg-r-likely: light-dark(color-mix(in srgb, #d9453b 80%, var(--panel2)), #903b3a);
  --seg-r-lean: #63353a;
  --seg-r-lean: light-dark(color-mix(in srgb, #d9453b 62%, var(--panel2)), #63353a);
  --seg-uncalled: #3a4452;
  --seg-uncalled: light-dark(#c4c9d0, #3a4452);
  /* the state-legislature bar's holdover and open fills (css/legislatures.css): the light values are the ones above; the dark values are lightened so each is >= 3:1 against the bar's track
     (the shared --seg-*-fixed / --seg-uncalled measured 2.5 / 2.6 / 1.8:1 there; WCAG 1.4.11). The Board and the needle keep the shared tokens. */
  --seg-d-hold: #4f77b5;
  --seg-d-hold: light-dark(color-mix(in srgb, #2b6fe0 55%, #6a6e76), #4f77b5);
  --seg-r-hold: #b0605a;
  --seg-r-hold: light-dark(color-mix(in srgb, #d9453b 55%, #6a6e76), #b0605a);
  --seg-open: #6b7a8e;
  --seg-open: light-dark(#c4c9d0, #6b7a8e);

  /* ---- compat aliases: the old names keep working, so ~600 var() uses need no edit ----------------------------------------- */
  --surface: var(--panel);
  --surface-2: var(--panel2);
  /* a 4% tint over panel2 (pressed nav item, hover, selected row). Carries --text --muted --d --r --link --gold --pro --tweets at >= 4.5:1;
     NOT --faint, --anti-text or --orgs (those are only promised on bg/panel/panel2/sunken): see js/core/contrast.selftest.mjs */
  --surface-3: color-mix(in srgb, var(--text) 4%, var(--panel2));
  --line: var(--border);
  --line-soft: color-mix(in srgb, var(--border) 60%, var(--panel));
  --font: var(--font-sans);
  --mono: var(--font-mono);
  --radius: var(--radius-lg);

  /* layout (unchanged) */
  --ticker-h: 30px;
  --gutter: 16px;
  --topbar-h: 76px; /* the pinned bar (nav + ticker); measured at runtime */
  --header-h: 130px; /* the whole header incl. the row that scrolls away; measured at runtime */
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-padding-top: calc(var(--topbar-h) + 12px);
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.45 var(--font);
  overflow-wrap: break-word;
}

a {
  color: var(--link);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 4px;
}

h1,
h2,
h3,
p {
  margin: 0;
}
h2 {
  font-size: 16px;
  font-weight: 650;
}
h3 {
  font-size: 14px;
  font-weight: 650;
}

.wrap {
  width: 100%;
  max-width: 1360px;
  margin: 0 auto;
  padding-inline: var(--gutter);
}
.muted {
  color: var(--muted);
}
.num {
  font-variant-numeric: tabular-nums;
}
.is-hidden {
  display: none !important;
}
[hidden] {
  display: none !important;
}
/* A live region that is display:none until the message exists is not reliably announced (WCAG 4.1.3). An EMPTY role=status / role=alert node that is
   [hidden] stays rendered, visually hidden, so the text written into it later is read out. Once it has text it is shown (or hidden) as usual. */
[role='status'][hidden]:empty,
[role='alert'][hidden]:empty {
  display: block !important;
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
.skip-link {
  position: absolute;
  left: -999px;
  top: 8px;
  z-index: 100;
  background: var(--surface-3);
  padding: 8px 12px;
  border-radius: 8px;
}
.skip-link:focus {
  left: 8px;
}

/* ---- top bar ------------------------------------------------------------------- */

.topbar {
  background: var(--panel);
  border-bottom: 1px solid var(--line-soft);
}
/* Simulated data: pinned to the top of the window at every scroll position and on phones too (the nav bar below it is static
   there). The nav bar and sticky table headers sit below it (--sim-h). #fff on #b3261e is 6.5:1. */
.sim-banner {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 2px 12px;
  padding: 5px 16px;
  background: var(--accent-fill);
  color: var(--on-gold);
  font-size: 13px;
  line-height: 1.35;
  text-align: center;
}
.sim-banner[hidden] {
  display: none;
}
.sim-banner strong {
  font-weight: 800;
  letter-spacing: 0.08em;
}
/* Nav + chamber odds + ticker: the part that stays pinned. Must be a direct child of <body> to stay sticky. */
.stickybar {
  position: sticky;
  top: var(--sim-h, 0px);
  z-index: 40;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
}
.topbar-inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 20px;
  min-height: 56px; /* not --topbar-h: that one is measured from this element */
  padding-block: 8px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
}
.brand:hover {
  text-decoration: none;
}
.brand-name {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.brand-mark {
  display: inline-flex;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  overflow: hidden;
}
.brand-mark i {
  flex: 1;
}
.brand-mark i:first-child {
  background: var(--d-fill);
}
.brand-mark i:last-child {
  background: var(--r-fill);
}
.nav {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  gap: 2px;
  overflow-x: auto;
  scrollbar-width: none;
}
.nav::-webkit-scrollbar {
  display: none;
}
.nav a {
  position: relative;
  flex: none;
  padding: 6px 10px;
  border-radius: 8px;
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}
.nav a:hover {
  color: var(--text);
  background: var(--surface-2);
  text-decoration: none;
}
.nav a[aria-current='page'],
.nav a[aria-current='true'] {
  color: var(--text);
  background: var(--surface-3);
}
.status-strip {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 6px 10px;
}
.clock {
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  min-width: 7.5em;
  text-align: right;
}
.countdown {
  color: var(--muted);
  font-size: 13px;
}
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--line);
  font-size: 12.5px;
  white-space: nowrap;
}
.pill .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--grey);
}
.pill[data-state='ok'] .dot {
  background: var(--ok);
}
.pill[data-state='warn'] .dot {
  background: var(--warn);
}
.pill[data-state='bad'] .dot {
  background: var(--bad);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 25%, transparent);
}
.pill[data-state='bad'] {
  border-color: var(--bad);
}
.pill[data-state='unknown'] .dot {
  background: transparent;
  border: 1.5px solid var(--grey);
}
.pill-env {
  color: var(--warn);
  border-color: var(--warn);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 11px;
}

.banner {
  margin-top: 12px;
  padding: 10px 14px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border: 1px solid var(--warn);
  color: var(--text);
}

.view {
  flex: 1;
  padding-block: 16px 40px;
  outline: none;
}
.loading,
.error-box {
  color: var(--muted);
  padding: 40px 0;
  text-align: center;
}
.error-box {
  color: var(--bad);
}
.site-foot {
  border-top: 1px solid var(--line-soft);
  font-size: 12.5px;
  color: var(--muted);
}
.foot-inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 4px 20px;
  padding-block: 14px 24px;
}
.foot-attrib {
  flex: 1 1 380px;
  min-width: 0;
}

/* ---- shell: needle chips, nav row, ticker, toasts (js/core) ---- */

.topbar-main {
  background: var(--panel);
}
.needles {
  display: flex;
  flex: none;
  gap: 8px;
}
.nchip {
  display: inline-grid;
  grid-template-columns: auto 44px auto;
  align-items: center;
  gap: 7px;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 12.5px;
  white-space: nowrap;
}
.nchip:hover {
  background: var(--surface-2);
  text-decoration: none;
}
.nchip-label {
  color: var(--muted);
  font-weight: 650;
}
.nchip-bar {
  display: block;
  height: 6px;
  overflow: hidden;
  border-radius: 3px;
  background: var(--r-fill);
}
.nchip-d {
  display: block;
  width: 50%;
  height: 100%;
  background: var(--d-fill);
  transition: width 0.5s ease;
}
.nchip-val {
  min-width: 3.9em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.nchip[data-side='D'] .nchip-val {
  color: var(--d);
}
.nchip[data-side='R'] .nchip-val {
  color: var(--r);
}
/* before the first vote the chip shows the published forecast, not a live reading: say so in words that everyone can see (the tooltip and the screen-reader description say it too) */
.nchip-fc {
  display: none;
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
}
.nchip[data-forecast='1'] {
  grid-template-columns: auto 44px auto auto;
}
.nchip[data-forecast='1'] .nchip-fc {
  display: inline;
}
.nchip[data-side='na'] .nchip-bar {
  background: var(--surface-3);
}
.nchip[data-side='na'] .nchip-d {
  background: transparent;
}
.nchip[data-side='na'] .nchip-val {
  color: var(--muted);
  font-weight: 400;
}

.nav-row {
  background: var(--panel);
}
.nav-inner {
  display: flex;
  align-items: center;
  gap: 6px 14px;
  padding-block: 4px;
}
.nav-badge {
  display: inline-block;
  min-width: 18px;
  margin-left: 6px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--accent-fill);
  color: var(--accent-ink);
  font-size: 11px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
}
.jump {
  flex: 0 1 200px;
  min-width: 130px;
}
.jump label {
  display: block;
}
.jump input {
  min-height: 30px;
  padding: 3px 10px;
  font-size: 13px;
}
/* Below ~1420px the nav (ten links since Measures and Legislatures joined it), the two needle chips and the jump box no longer fit on one row: nav gets its own row. */
@media (max-width: 1420px) {
  .nav-inner {
    flex-wrap: wrap;
  }
  .nav {
    flex: 1 1 100%;
  }
  .jump {
    flex: 1 1 200px;
    margin-left: auto;
    max-width: 320px;
  }
}

.ticker {
  border-top: 1px solid var(--line-soft);
  background: var(--sunken);
  font-size: 12.5px;
}
.ticker-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  height: var(--ticker-h);
}
.ticker-label {
  flex: none;
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
.ticker-viewport {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14px, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(90deg, transparent, #000 14px, #000 calc(100% - 24px), transparent);
}
.ticker-track {
  display: inline-flex;
  align-items: center;
  gap: 28px;
  padding-left: 14px;
  white-space: nowrap;
}
.ticker.is-scrolling .ticker-track {
  animation: tk-scroll var(--tk-dur, 60s) linear infinite;
}
.ticker.is-scrolling .ticker-viewport:hover .ticker-track {
  animation-play-state: paused;
}
/* keyboard focus inside the ticker: hold the row still at its start so the focused link can be scrolled fully into view (js/core/alerts-ui.js) */
.ticker.is-scrolling .ticker-track:focus-within {
  animation: none;
}
.ticker.is-scrolling .ticker-viewport:has(.ticker-track:focus-within) {
  overflow-x: auto;
}
/* WCAG 2.2.2: the Pause button. Paused = no animation, no duplicate loop copy, a plain scrolling row. */
.ticker.is-paused .ticker-track {
  animation: none;
}
.ticker.is-paused .tk-dup {
  display: none;
}
.ticker.is-paused .ticker-viewport {
  overflow-x: auto;
  scrollbar-width: thin;
}
@keyframes tk-scroll {
  to {
    transform: translateX(var(--tk-shift, -50%));
  }
}
.tk-dup {
  display: inline-flex;
  align-items: center;
  gap: 28px;
}
.tk {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--text);
}
a.tk:hover {
  text-decoration: none;
}
a.tk:hover .tk-text {
  text-decoration: underline;
}
.tk time {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.tk-tag {
  padding: 0 6px;
  border-radius: 4px;
  background: var(--surface-3);
  font-size: 11px;
  line-height: 17px;
}
.tk-tag.tk-D {
  background: var(--d-fill);
  color: #fff;
}
.tk-tag.tk-R {
  background: var(--r-badge);
  color: #fff;
}
.tk-critical .tk-text b {
  color: var(--accent);
}
.tk-warn .tk-text b {
  color: var(--warn);
}
.tk-empty {
  color: var(--muted);
}
/* The empty ticker (before the first alert) is one placeholder sentence, longer than the viewport on a phone. When motion is reduced or the ticker is paused the
   viewport scrolls sideways (overflow-x: auto), and a scroll region with nothing focusable inside fails WCAG 2.1.1 (axe scrollable-region-focusable) on every page
   until the first alert. The sentence is not essential and a screen reader reads all of it, so the empty state clips like the moving ticker does and does not scroll. */
.ticker .ticker-viewport:has(> .ticker-track > .tk-empty) {
  overflow: hidden;
}
.ticker-btn {
  flex: none;
  min-height: 24px;
  padding: 1px 9px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font-size: 11.5px;
  cursor: pointer;
}
.ticker-mute[aria-pressed='true'] {
  color: var(--text);
}
.ticker-btn:hover {
  background: var(--surface-2);
}
.ticker.is-paused .ticker-pause {
  color: var(--text);
  border-color: var(--gold);
}
@media (prefers-reduced-motion: reduce) {
  .ticker.is-scrolling .ticker-track {
    animation: none;
  }
  .ticker-viewport {
    overflow-x: auto;
  }
  .nchip-d,
  .seg,
  .cand-bar i {
    transition: none;
  }
  .ev-new,
  .toast {
    animation: none;
  }
}

.toasts {
  position: fixed;
  right: 16px;
  bottom: 16px;
  z-index: 60;
  display: grid;
  gap: 8px;
  width: min(380px, calc(100vw - 32px));
  /* never more than a quarter of the window (WCAG 2.4.11 / 1.4.10): a longer stack scrolls inside itself, newest at the bottom */
  max-height: 25vh;
  max-height: 25dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  pointer-events: none;
}
.toasts:empty {
  display: none;
}
/* pad the end of the page and the scroll-into-view margin by the stack height (set by core/alerts-ui.js) so a focused control is never hidden under a toast */
html {
  scroll-padding-bottom: var(--toasts-h, 0px);
}
body {
  padding-bottom: var(--toasts-h, 0px);
}
.toast {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 8px 10px 12px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--grey);
  border-radius: 10px;
  background: var(--panel);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55);
  animation: toast-in 0.22s ease-out;
  pointer-events: auto;
}
.toast-D {
  border-left-color: var(--d-fill);
}
.toast-R {
  border-left-color: var(--r-fill);
}
.toast-warn {
  border-left-color: var(--warn);
}
.toast-critical {
  border-color: var(--accent);
}
.toast-main {
  display: grid;
  flex: 1;
  gap: 2px;
  min-width: 0;
  color: var(--text);
}
.toast-main:hover {
  text-decoration: none;
}
.toast-title {
  font-weight: 700;
}
.toast-text {
  color: var(--muted);
  font-size: 13px;
}
.toast-x {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  min-width: 32px; /* WCAG 2.5.8: at least 24 x 24 CSS px */
  min-height: 32px;
  margin: -4px 0;
  padding: 0 6px;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
}
.toast-x:hover {
  color: var(--text);
}
.toast.is-leaving {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.2s, transform 0.2s;
}
@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

.linklike {
  padding: 0;
  border: 0;
  background: none;
  color: var(--link);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.dlg {
  width: min(720px, calc(100vw - 32px));
  max-height: 82vh;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
}
.dlg::backdrop {
  background: rgba(0, 0, 0, 0.6);
}
.dlg-body {
  max-height: 82vh;
  padding: 16px 18px;
  overflow: auto;
}
.attr-list {
  display: grid;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.attr-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
}
.attr-lic {
  max-width: 100%;
  padding: 1px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--muted);
  font-size: 11.5px;
}
.attr-item p {
  margin-top: 3px;
  font-size: 13px;
}

/* Layouts: the route table (or ctx.setLayout) picks one. */
.view[data-layout='wide'] {
  max-width: none;
}
.view[data-layout='full'] {
  max-width: none;
  padding: 0;
}
body[data-layout='full'] .view {
  min-height: calc(100vh - var(--header-h));
  min-height: calc(100dvh - var(--header-h));
}

/* ---- shared page components (every module may use these) ---- */

.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px 16px;
  margin-bottom: 14px;
}
.page-head h2 {
  font-size: 22px;
  font-weight: 750;
}
.page-head .muted {
  font-size: 13.5px;
}
.subhead {
  margin: 14px 0 4px;
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
}
.kpi {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface-2);
}
.kpi-label {
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.kpi-value {
  font-size: 24px;
  font-weight: 750;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
}
.kpi-sub {
  color: var(--muted);
  font-size: 12.5px;
}
.lg-bins {
  display: flex;
  margin: 8px 0 0;
  padding: 0;
  overflow: hidden;
  border-radius: 5px;
  list-style: none;
  font-size: 11px;
}
.lg-bins li {
  flex: 1 1 0;
  padding: 3px 4px;
  text-align: center;
  white-space: nowrap;
}
.skeleton i {
  display: block;
  height: 14px;
  margin: 10px 0;
  border-radius: 6px;
  background: linear-gradient(90deg, var(--surface-2), var(--surface-3), var(--surface-2)) 0 0 / 200% 100%;
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer {
  to {
    background-position: -200% 0;
  }
}
.spark {
  vertical-align: middle;
}
.notfound {
  display: grid;
  gap: 8px;
  max-width: 560px;
  margin: 40px auto;
}

.stub {
  max-width: 880px;
}
.view[data-layout='full'] > .stub {
  margin: 16px;
}
.stub p {
  margin-top: 6px;
}
.stub code {
  font-family: var(--mono);
  font-size: 12.5px;
}
.stub-todos {
  display: grid;
  gap: 4px;
  margin: 4px 0 0;
  padding-left: 20px;
}
.stub-needs {
  display: grid;
  gap: 4px;
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
}
.stub-state {
  display: inline-block;
  min-width: 6.5em;
  font-size: 12px;
  font-weight: 650;
}
.stub-state[data-state='ok'] {
  color: var(--ok);
}
.stub-state[data-state='bad'] {
  color: var(--warn);
}

/* ---- cards, buttons, form controls ----------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  min-width: 0;
}
.card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px 12px;
  margin-bottom: 10px;
}
.card-head .muted {
  font-size: 13px;
}

button {
  font: inherit;
  color: inherit;
}
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 36px;
  padding: 6px 14px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover {
  background: var(--surface-3);
}
.btn[disabled],
.btn[aria-disabled='true'],
.switch[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
}
.btn-primary {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-gold);
}
.btn-primary:hover {
  background: var(--accent-fill);
  filter: brightness(1.06);
}
.btn-danger {
  background: transparent;
  border-color: var(--bad);
  color: var(--bad);
}
.btn-danger:hover {
  background: color-mix(in srgb, var(--bad) 15%, transparent);
}
.btn-ghost {
  background: transparent;
}
.btn-sm {
  min-height: 30px;
  padding: 3px 10px;
  font-size: 13px;
}
.btn-icon {
  min-width: 36px;
  padding: 6px 8px;
}

input[type='search'],
input[type='text'],
input[type='number'],
input[type='password'],
input[type='url'],
select,
textarea {
  width: 100%;
  min-height: 36px;
  padding: 6px 10px;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  font: inherit;
}
textarea {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.45;
  resize: vertical;
}
input::placeholder,
textarea::placeholder {
  color: var(--faint);
}
input:focus,
select:focus,
textarea:focus {
  outline: 2px solid var(--focus);
  outline-offset: 0;
  border-color: transparent;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 11.5px;
  color: var(--muted);
  min-width: 0;
}
.field > span,
.field label > span {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
}
.field-grow {
  flex: 1 1 240px;
}

/* ---- chips, tags, margins ----------------------------------------------------------- */

.m {
  font-weight: 650;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.m-D {
  color: var(--d);
}
.m-R {
  color: var(--r);
}
.m-even {
  color: var(--text);
}
.m-na {
  color: var(--muted);
  font-weight: 400;
}

.pty-text {
  display: inline-block;
  min-width: 1.35em;
  padding: 0 4px;
  border-radius: 4px;
  text-align: center;
  font-size: 0.92em;
  color: #fff;
}
.pty-text.pty-D {
  background: var(--d-fill);
}
.pty-text.pty-R {
  background: var(--r-badge);
}
.pty {
  display: inline-block;
  min-width: 1.6em;
  padding: 0 5px;
  border-radius: 4px;
  text-align: center;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: var(--grey);
}
.pty-D {
  background: var(--d-fill);
}
.pty-R {
  background: var(--r-badge);
}
.pty-I {
  background: var(--grey);
}
.pty-L {
  background: var(--grey);
}
.pty-G {
  background: var(--grey);
}
.pty-O {
  background: var(--grey);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 12px;
  font-weight: 650;
  white-space: nowrap;
}
.chip-lg {
  font-size: 14px;
  padding: 4px 13px;
}
.chip-lean {
  color: var(--text);
  border-style: dashed;
  border-color: var(--control-border);
}
.chip-likely {
  color: var(--text);
  border-color: var(--control-border);
}
.chip-projected {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent-fill) 10%, transparent);
}
.chip-final {
  color: var(--accent-ink);
  background: var(--accent-fill);
  border-color: var(--accent);
}

.tag {
  display: inline-block;
  margin: 2px 4px 0 0;
  padding: 0 6px;
  border: 1px solid var(--line);
  border-radius: 4px;
  color: var(--muted);
  font-size: 10.5px;
  line-height: 16px;
  white-space: nowrap;
  cursor: help;
}
.tag-warn {
  color: var(--warn);
  border-color: var(--warn);
}
.tag-demo {
  color: var(--spend);
  border-color: var(--spend);
}

.tier {
  font-size: 12px;
  color: var(--muted);
}
.tier-core {
  color: var(--text);
  font-weight: 600;
}

.rates {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
}
.rate {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 7px;
  border-radius: 6px;
  border: 1px solid var(--line);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 650;
  white-space: nowrap;
  cursor: help;
}
.rate i {
  font-style: normal;
  color: var(--text); /* neutral, not the party tone: the initial (C/S/I) names the rater and must read on every tinted chip */
  font-weight: 700;
}
.rate-toss {
  color: var(--text);
  background: var(--panel2);
}
.rate-D {
  color: var(--d);
  background: color-mix(in srgb, var(--d-fill) 10%, transparent);
  border-color: var(--d);
}
.rate-R {
  color: var(--r);
  background: color-mix(in srgb, var(--r-fill) 10%, transparent);
  border-color: var(--r);
}
.rate-likely.rate-D {
  background: color-mix(in srgb, var(--d-fill) 20%, transparent);
  color: var(--d);
}
.rate-likely.rate-R {
  background: color-mix(in srgb, var(--r-fill) 20%, transparent);
  color: var(--r);
}
.rate-solid.rate-D {
  background: color-mix(in srgb, var(--d-fill) 32%, transparent);
  color: var(--d);
}
.rate-solid.rate-R {
  background: color-mix(in srgb, var(--r-fill) 32%, transparent);
  color: var(--r);
}

.badge {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 11px;
  font-weight: 650;
  white-space: nowrap;
}
.badge-status {
  color: var(--accent);
  border-color: var(--accent);
}
.badge-lead_change {
  color: var(--spend);
  border-color: var(--spend);
}
.badge-projected {
  color: var(--accent-ink);
  background: var(--accent-fill);
  border-color: var(--accent);
}
.badge-first_votes {
  color: var(--ok);
  border-color: var(--ok);
}
.badge-bellwether {
  color: var(--orgs);
  border-color: var(--orgs);
}
.badge-forecast_miss {
  color: var(--warn);
  border-color: var(--warn);
}
.badge-tipping {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent-fill) 10%, transparent);
}
.ev-sev-major .ev-msg {
  font-weight: 650;
}

/* ---- board: balance of power ------------------------------------------------------------ */

.board-top {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: 16px;
  margin-bottom: 16px;
}
.bop-stack {
  display: grid;
  gap: 16px;
  min-width: 0;
}
.bop-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}
.bop-need {
  font-size: 13px;
}
.bop-score {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  gap: 12px;
}
.bop-side.bop-r {
  text-align: right;
}
.bop-num {
  font-size: 36px;
  font-weight: 750;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}
.bop-d .bop-num {
  color: var(--d);
}
.bop-r .bop-num {
  color: var(--r);
}
.bop-sub {
  margin-top: 3px;
  font-size: 12px;
  color: var(--muted);
}
.bop-sub span {
  display: block;
}
.bop-sub span:empty {
  display: none;
}
.bop-center {
  text-align: center;
  font-size: 12.5px;
  padding-bottom: 2px;
}
.bop-mid {
  font-weight: 650;
  font-size: 14px;
}
.bop-bar {
  position: relative;
  margin-top: 26px;
  height: 26px;
}
.bop-segs {
  display: flex;
  height: 100%;
  border-radius: 7px;
  overflow: hidden;
  background: var(--surface-3);
}
.seg {
  flex: 0 1 0;
  min-width: 0;
  height: 100%;
  transition: flex-grow 0.5s ease;
}
.seg + .seg:not(.is-empty) {
  box-shadow: inset 1px 0 0 color-mix(in srgb, var(--panel) 55%, transparent);
}
.seg-d-fixed {
  background: var(--seg-d-fixed);
}
.seg-d-called {
  background: var(--seg-d-called);
}
.seg-d-likely {
  background: var(--seg-d-likely);
}
.seg-d-lean {
  background: var(--seg-d-lean);
}
.seg-r-fixed {
  background: var(--seg-r-fixed);
}
.seg-r-called {
  background: var(--seg-r-called);
}
.seg-r-likely {
  background: var(--seg-r-likely);
}
.seg-r-lean {
  background: var(--seg-r-lean);
}
.seg-mid-uncalled-l,
.seg-mid-uncalled-r {
  background: var(--seg-uncalled);
}
.seg-mid-untracked {
  background: repeating-linear-gradient(135deg, var(--panel2) 0 5px, var(--border) 5px 10px);
}
.seg-d-lean,
.seg-r-lean {
  background-image: repeating-linear-gradient(135deg, color-mix(in srgb, var(--text) 16%, transparent) 0 2px, transparent 2px 6px);
}
.bop-line {
  position: absolute;
  left: 50%;
  top: -8px;
  bottom: -8px;
  width: 2px;
  margin-left: -1px;
  background: var(--text);
  border-radius: 1px;
}
.bop-line b {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 2px;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
}
.bop-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  font-size: 11.5px;
  color: var(--muted);
}
.bop-legend li {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.sw {
  display: inline-block;
  width: 18px;
  height: 9px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--seg-d-called) 50%, var(--seg-r-called) 50%);
}
.sw-likely {
  background: linear-gradient(90deg, var(--seg-d-likely) 50%, var(--seg-r-likely) 50%);
}
.sw-lean {
  background: linear-gradient(90deg, var(--seg-d-lean) 50%, var(--seg-r-lean) 50%);
}
.sw-uncalled {
  background: var(--seg-uncalled);
}
.sw-untracked {
  background: repeating-linear-gradient(135deg, var(--panel2) 0 3px, var(--border) 3px 6px);
}
.bop-note {
  margin-top: 8px;
  font-size: 12.5px;
}

/* ---- feed ---------------------------------------------------------------------------------- */

.feed-panel {
  display: flex;
  flex-direction: column;
}
.feed-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Narrow list (the board panel): badge and time on one line, the message under it. */
.ev {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: 'badge time' 'body body';
  align-items: center;
  gap: 3px 10px;
  padding: 9px 0;
  border-top: 1px solid var(--line-soft);
  font-size: 13.5px;
}
.ev:first-child {
  border-top: 0;
}
.ev .badge {
  grid-area: badge;
  justify-self: start;
}
.ev-time {
  grid-area: time;
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ev-body {
  grid-area: body;
  min-width: 0;
}
/* Wide list (the Feed page): one line per event. */
@media (min-width: 761px) {
  .feed-wide .ev {
    grid-template-columns: 8.5em 6.4em minmax(0, 1fr);
    grid-template-areas: 'time badge body';
    gap: 2px 14px;
  }
  .feed-wide .ev-time {
    text-align: right;
  }
}
.ev-race {
  font-weight: 600;
}
.ev-new {
  animation: flash 2.4s ease-out;
}
@keyframes flash {
  from {
    background: color-mix(in srgb, var(--accent-fill) 22%, transparent);
  }
  to {
    background: transparent;
  }
}
.empty {
  padding: 18px 0;
  text-align: center;
}

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

table.data {
  width: 100%;
  border-collapse: collapse;
}
.data th {
  position: sticky;
  top: var(--topbar-h);
  z-index: 3;
  background: var(--surface);
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}
.data td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}
.data .num {
  text-align: right;
}
.th-sort {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
.th-sort:hover {
  color: var(--text);
}
th[aria-sort='ascending'] .th-sort::after {
  content: ' ▲';
  font-size: 0.8em;
}
th[aria-sort='descending'] .th-sort::after {
  content: ' ▼';
  font-size: 0.8em;
}
th[aria-sort='ascending'],
th[aria-sort='descending'] {
  color: var(--text);
}

/* ---- board: key races table ------------------------------------------------------------------ */

.races {
  padding: 14px 0 6px;
}
.races > .card-head,
.races .toolbar,
.races > .empty {
  padding-inline: 16px;
}
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 10px;
  padding-bottom: 12px;
}
.toolbar .field select {
  min-width: 7.5em;
}
.field-sort {
  flex-direction: row;
  align-items: end;
  gap: 6px;
}
.field-sort label {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.races-table .c-name {
  min-width: 190px;
}
.races-table .c-name .race-link {
  color: var(--text);
  font-weight: 650;
}
.races-table .c-name .sub {
  font-size: 12px;
  color: var(--muted);
}
.races-table .sub-tier,
.races-table .sub-state {
  display: none;
}
.c-tier .st {
  display: block;
  font-weight: 700;
  line-height: 1.2;
}
.race-row {
  cursor: pointer;
}
.race-row:hover td {
  background: color-mix(in srgb, var(--text) 4%, transparent);
}
.race-row.st-final td:first-child {
  box-shadow: inset 3px 0 0 var(--accent);
}
.race-row.st-projected td:first-child {
  box-shadow: inset 3px 0 0 color-mix(in srgb, var(--accent-fill) 60%, transparent);
}
.c-pct {
  min-width: 78px;
}
.meter {
  display: block;
  height: 3px;
  margin-top: 3px;
  border-radius: 2px;
  background: var(--surface-3);
}
.meter i {
  display: block;
  height: 100%;
  border-radius: 2px;
  background: var(--mid);
}
.data small {
  display: block;
  color: var(--muted);
  font-size: 11px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}
.proj-base .m {
  /* forecast-only margin: set apart by italics and weight, not by opacity (dimmed party colours fell to 2.7:1) */
  font-weight: 500;
  font-style: italic;
}
.proj-base .m-D {
  color: var(--d);
}
.proj-base .m-R {
  color: var(--r);
}
.need b {
  font-weight: 700;
}
.need-D b {
  color: var(--d);
}
.need-R b {
  color: var(--r);
}
.need-out {
  /* "needs more than all of what is left": set apart by weight, not opacity (the dimmed party colours were 3.6:1) */
  font-style: italic;
}
.c-need {
  white-space: nowrap;
}

/* table -> cards below 880px */
@media (max-width: 1100px) {
  .races-table .c-tier,
  .races-table .c-pres {
    display: none;
  }
  .races-table .sub-tier,
  .races-table .sub-state {
    display: inline;
  }
}
@media (max-width: 880px) {
  .races-table thead {
    display: none;
  }
  .races-table,
  .races-table tbody {
    display: block;
  }
  .races-table tbody {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
    gap: 10px;
    padding: 0 12px 10px;
  }
  .races-table tr.race-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px 10px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-2);
  }
  .races-table td {
    display: block;
    padding: 0;
    border: 0;
    text-align: left;
    min-width: 0;
  }
  .races-table td::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 1px;
    color: var(--muted);
    font-size: 10.5px;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-transform: uppercase;
  }
  .races-table .c-name {
    grid-column: 1 / 3;
    grid-row: 1;
    min-width: 0;
  }
  .races-table .c-name::before,
  .races-table .c-status::before,
  .races-table .c-rate::before {
    display: none;
  }
  .races-table .c-status {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }
  .races-table .c-rate {
    grid-column: 1 / -1;
    grid-row: 2;
  }
  .races-table .c-tier {
    display: none;
  }
  .races-table .c-pres {
    display: block;
  }
  .races-table .num {
    text-align: left;
  }
  .race-row.st-final,
  .race-row.st-projected {
    border-left-width: 3px;
    border-left-color: var(--accent);
  }
  .race-row.st-final td:first-child,
  .race-row.st-projected td:first-child {
    box-shadow: none;
  }
  .race-row:hover td {
    background: none;
  }
}

/* ---- layout breakpoints --------------------------------------------------------------------------- */

@media (min-width: 761px) {
  :root {
    --gutter: 24px;
  }
}
@media (max-width: 980px) {
  .board-top {
    grid-template-columns: minmax(0, 1fr);
  }
}
@media (max-width: 760px) {
  :root {
    --gutter: 16px;
  }
  .stickybar {
    position: static;
  }
  .data th {
    position: static;
  }
  .brand-name {
    font-size: 16px;
  }
  .brand-mark {
    display: none;
  }
  .topbar-inner {
    column-gap: 10px;
  }
  .pill {
    padding: 2px 9px;
    font-size: 12px;
  }
  .nav a {
    padding: 6px 9px;
  }
  .nav-inner {
    flex-wrap: wrap;
  }
  .jump {
    flex: 1 1 100%;
    margin-bottom: 4px;
  }
  .toasts {
    right: 8px;
    bottom: 8px;
  }
  .status-strip {
    flex: 1 0 100%;
    margin-left: 0;
    justify-content: flex-start;
  }
  .clock {
    text-align: left;
    min-width: 0;
  }
  .bop-num {
    font-size: 30px;
  }
  /* Phone: the two totals share the first row, the undecided summary sits under them. */
  .bop-score {
    grid-template-columns: 1fr 1fr;
    gap: 6px 12px;
  }
  .bop-center {
    order: 3;
    grid-column: 1 / -1;
    padding: 2px 0 0;
  }
  .toolbar .field {
    flex: 1 1 calc(50% - 10px);
  }
  .toolbar .field-grow {
    flex-basis: 100%;
  }
  .field-sort {
    flex: 1 1 100%;
  }
  .field-sort label {
    flex: 1;
  }
}

/* ---- feed page -------------------------------------------------------------------------------- */

.feed-page {
  padding-bottom: 8px;
}
.ev-clock {
  margin-right: 0.5em;
  color: var(--text);
  font-weight: 600;
}
.feed-wide .ev-time {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
@media (min-width: 761px) {
  .feed-wide .ev-time {
    align-items: flex-end;
  }
  .feed-wide .ev-clock {
    margin-right: 0;
  }
}
@media (max-width: 760px) {
  .feed-wide .ev-time {
    flex-direction: row;
    align-items: baseline;
  }
}
.feed-more {
  padding: 12px 0 4px;
  text-align: center;
}

/* ---- shared components: map and chart (js/map.js, js/chart.js, map explorer) ---------------------- */

.seg-toggle {
  display: inline-flex;
  flex-wrap: wrap;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.seg-toggle button {
  min-height: 32px;
  padding: 4px 11px;
  border: 0;
  border-right: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.seg-toggle button:last-child {
  border-right: 0;
}
.seg-toggle button:hover {
  color: var(--text);
}
.seg-toggle button[aria-pressed='true'] {
  background: var(--accent-fill);
  color: var(--on-gold);
}
.map-wrap {
  position: relative;
  min-height: 80px;
}
.county-map {
  display: block;
  width: 100%;
  height: auto;
  max-height: 600px;
}
.county-map path {
  stroke: var(--bg);
  stroke-width: 0.9px;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: fill 0.35s ease;
}
.county-map path.is-hl {
  stroke: var(--map-ink);
  stroke-width: 2.2px;
}
.map-tip,
.chart-tip {
  position: absolute;
  z-index: 6;
  max-width: 270px;
  padding: 8px 11px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55);
  font-size: 12.5px;
  line-height: 1.45;
  pointer-events: auto; /* WCAG 1.4.13: hoverable (js/core/ui.js createTooltip) */
}
.tip-title {
  margin-bottom: 2px;
  font-weight: 700;
}
.tip-done {
  margin-left: 4px;
  padding: 0 5px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--ok) 18%, transparent);
  color: var(--ok);
  font-size: 11px;
}
.map-msg,
.map-note {
  margin-top: 10px;
  font-size: 12.5px;
}
.map-legend {
  display: grid;
  grid-template-columns: minmax(150px, 280px) auto;
  align-items: center;
  gap: 4px 20px;
  margin-top: 12px;
  color: var(--muted);
  font-size: 12px;
}
.lg-bar {
  grid-column: 1;
  height: 10px;
  border-radius: 5px;
}
.lg-scale {
  grid-column: 1;
  display: flex;
  justify-content: space-between;
}
.lg-none {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  align-items: center;
  gap: 6px;
}
.lg-hatch {
  display: inline-block;
  width: 20px;
  height: 12px;
  border-radius: 2px;
  background: repeating-linear-gradient(135deg, var(--nodata-line) 0 2px, var(--nodata-base) 2px 5px);
}

.chart-wrap {
  position: relative;
  min-height: 200px;
}
.history-chart {
  display: block;
  width: 100%;
  height: auto;
  touch-action: pan-y;
}
.history-chart .zone-d {
  fill: color-mix(in srgb, var(--d-fill) 8%, transparent);
}
.history-chart .zone-r {
  fill: color-mix(in srgb, var(--r-fill) 8%, transparent);
}
.history-chart .axis text {
  /* --text, not --muted: the labels sit over the red/blue zone tints and the projection band, where --muted measured 4.23:1 in the dark theme (WCAG 1.4.3) */
  fill: var(--text);
  font-size: 11px;
}
.history-chart .axis path {
  stroke: var(--line);
}
.history-chart .axis line {
  stroke: var(--line-soft);
}
.history-chart .axis-y .domain {
  display: none;
}
.history-chart .zero-line {
  stroke: var(--faint);
  stroke-dasharray: 3 3;
}
.history-chart .band-area {
  fill: color-mix(in srgb, var(--accent-fill) 20%, transparent);
}
.history-chart .line-proj {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
}
.history-chart .line-now {
  fill: none;
  stroke: var(--text);
  stroke-width: 1.8;
  stroke-dasharray: 5 3;
}
.history-chart .dot-proj {
  fill: var(--accent-fill);
}
.history-chart .dot-now {
  fill: var(--text);
}
.history-chart .crosshair {
  stroke: var(--muted);
  stroke-width: 1;
}
.history-chart .chart-overlay {
  fill: transparent;
  cursor: crosshair;
}

.lbl-short {
  display: none;
}
@media (max-width: 760px) {
  .lbl-short {
    display: inline;
  }
}

/* Responsive helpers shared by every table-bearing view. */
@media (max-width: 900px) {
  .hide-md {
    display: none;
  }
}
@media (max-width: 760px) {
  .hide-sm {
    display: none;
  }
  .lbl-long {
    display: none;
  }
  .seg-toggle button {
    padding: 4px 9px;
    font-size: 12.5px;
  }
}

/* ---- admin -------------------------------------------------------------------------------------- */

.admin-token {
  margin-bottom: 16px;
}
.admin-panels {
  display: grid;
  gap: 16px;
  min-width: 0;
}
.inline-form {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 10px;
}
.small {
  font-size: 12.5px;
}
.admin-token .small {
  margin-top: 8px;
}
.tok-ok {
  color: var(--ok);
  font-size: 13px;
}
.tok-bad {
  color: var(--bad);
  font-size: 13px;
}
.controls-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
}
.poll-state {
  display: flex;
  align-items: center;
  gap: 12px;
}
.poll-dot {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--grey);
}
.poll-dot.on {
  background: var(--ok);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 20%, transparent);
}
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.form-msg {
  font-size: 13px;
}
p.form-msg {
  margin-top: 10px;
}
.msg-ok {
  color: var(--ok);
}
.msg-error {
  color: var(--bad);
}
.msg-info {
  color: var(--muted);
}
.scrape-log {
  display: grid;
  gap: 3px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--muted);
}
.scrape-log .bad {
  color: var(--bad);
}
.sources-table .c-src {
  min-width: 190px;
}
.src-url {
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
}
.switch {
  min-width: 52px;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface-3);
  color: var(--muted);
  font-weight: 700;
  cursor: pointer;
}
.switch.on {
  border-color: var(--ok);
  background: color-mix(in srgb, var(--ok) 16%, transparent);
  color: var(--ok);
}
.bad {
  color: var(--bad);
}
.abs {
  display: block;
  color: var(--muted);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.err {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  max-width: 340px;
  overflow: hidden;
  color: var(--bad);
  font-size: 12.5px;
  overflow-wrap: anywhere;
  cursor: pointer;
}
.err.is-open {
  display: block;
  max-width: none;
}
/* a disabled source is marked with a "disabled" tag (text) and a dashed edge, never by dimming: opacity .6 dropped the text to 3.1:1 while the row still holds the "Off" button you must press to enable it (WCAG 1.4.3) */
.src-row.is-off td:first-child {
  box-shadow: inset 3px 0 0 var(--control-border);
}
.src-row.is-off .src-url,
.src-row.is-off .ad-adapter {
  font-style: italic;
}
.src-row.is-failing td:first-child,
tr.is-failing td:first-child {
  box-shadow: inset 3px 0 0 var(--bad);
}
.src-edit td {
  padding: 14px;
  background: var(--surface-2);
}
.edit-form,
.grid-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px;
}
.edit-form .field-grow {
  grid-column: span 2;
}
.field-wide {
  grid-column: span 2;
}
.field-full {
  grid-column: 1 / -1;
}
.form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  grid-column: 1 / -1;
}
.grid-form > .field > select,
.grid-form > .field > input[type='search'] {
  margin-top: 2px;
}
.manual-result {
  margin-top: 14px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-2);
}
.manual-result h3 {
  margin-bottom: 8px;
}
.mr-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 18px;
}
.settings-form + .settings-form {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line-soft);
}
.settings-form h3 {
  margin-bottom: 4px;
}
.bop-inputs {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin: 12px 0;
}
.bop-inputs fieldset {
  display: flex;
  gap: 12px;
  margin: 0;
  padding: 6px 14px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.bop-inputs legend {
  padding: 0 6px;
  font-weight: 650;
}
.bop-inputs .field input {
  width: 110px;
}
.settings-form textarea {
  margin: 8px 0 12px;
  min-height: 140px;
}
.param-ref {
  margin-top: 10px;
  font-size: 13px;
}
.param-ref summary,
.recent summary {
  cursor: pointer;
  color: var(--muted);
  font-weight: 600;
}
.param-ref dl {
  display: grid;
  gap: 8px;
  margin: 10px 0 0;
}
.param-ref dt {
  font-weight: 600;
}
.param-ref dd {
  margin: 0;
  color: var(--muted);
}
code {
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--surface-3);
  font-family: var(--mono);
  font-size: 12.5px;
}
.recent summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  list-style: none;
}
.recent summary::-webkit-details-marker {
  display: none;
}
.recent summary h2 {
  display: inline;
  color: var(--text);
}
.recent .table-wrap {
  margin-top: 10px;
}

/* admin tables (data-label cells) become cards below 1100px: nine columns need real width */
@media (max-width: 1100px) {
  table.stack thead {
    display: none;
  }
  table.stack,
  table.stack tbody {
    display: block;
  }
  table.stack tr {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 6px 12px;
    margin: 0 0 10px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-2);
  }
  table.stack td {
    display: block;
    min-width: 0;
    padding: 0;
    border: 0;
    text-align: left;
  }
  table.stack td::before {
    content: attr(data-label);
    display: block;
    color: var(--muted);
    font-size: 10.5px;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-transform: uppercase;
  }
  table.stack td.c-src,
  table.stack td.c-err,
  table.stack td.c-act {
    grid-column: 1 / -1;
  }
  table.stack td.c-src::before {
    display: none;
  }
  table.stack td.num {
    text-align: left;
  }
  table.stack tr.src-edit {
    display: block;
    margin-top: -6px;
  }
  table.stack td[colspan] {
    display: block;
  }
  table.stack td[colspan]::before {
    display: none;
  }
  .src-row.is-failing td:first-child,
  tr.is-failing td:first-child {
    box-shadow: none;
  }
  tr.is-failing,
  .src-row.is-failing {
    border-left: 3px solid var(--bad);
  }
  .err {
    max-width: none;
  }
  .src-url {
    max-width: none;
  }
  .edit-form .field-grow,
  .field-wide {
    grid-column: 1 / -1;
  }
}

@media (max-width: 480px) {
  .countdown {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- touch targets (WCAG 2.2 SC 2.5.8: at least 24 x 24 CSS px) ------------------------------------------------------------------
   Touch screens and narrow windows only. Inline links get the room from padding that an equal negative margin cancels, so the
   layout does not move; boxed controls just get a minimum height. */
@media (pointer: coarse), (max-width: 760px) {
  a.race-link,
  .al-kr-label,
  .bd-nc-link,
  .bd-card-foot a,
  .bd-card-head > a,
  .bd-map-open,
  .linklike,
  .ticker-label,
  .foot-attrib a {
    padding: 6px 5px;
    margin: -6px -5px;
  }
  .bd-pc-states {
    gap: 4px 6px;
  }
  .bd-pc-states a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    min-height: 32px;
  }
  .brand {
    min-height: 32px;
  }
  .ticker-btn {
    min-height: 32px;
  }
  .tk {
    min-height: 28px;
  }
  /* vertical room only: inline links in lists, tables and cards */
  a.ev-race,
  .crumbs a,
  .rv-zoomhint a,
  .card-head > a,
  .kpi-sub a,
  .al-title,
  .al-kr-all,
  .nd-dec-name,
  .nd-race-links a,
  .nd-decide-foot a,
  .tp-race,
  .tp-toprow a,
  .bw-cname a,
  .bw-blurb a,
  td a:not(.btn),
  .mx-crumbs a,
  .mx-panel a:not(.btn) {
    padding-block: 6px;
    margin-block: -6px;
  }
  /* tiny labels (a state code, "TX page"): room on every side */
  a.al-state,
  a.tp-st,
  .tp-tags a {
    padding: 6px 5px;
    margin: -6px -5px;
  }
  button.row-toggle {
    min-height: 28px;
    min-width: 28px;
  }
  button.th-sort {
    min-height: 28px;
    min-width: 28px;
    padding-block: 6px;
  }
  summary {
    padding-block: 5px;
  }
  .al-switch > input[type],
  .al-check input[type] {
    width: 24px;
    height: 24px;
  }
  a.al-state,
  a.mx-more {
    display: inline-block;
    min-width: 28px;
    text-align: center;
  }
  .view a:not([class]) {
    padding-block: 6px;
    margin-block: -6px;
  }
}


/* ---- footer switches: pause live updates, refresh now, needle wobble, the "/" key (js/core/controls.js) -------------------------------- */
.foot-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-block: 0 20px;
}
/* ---- the "More pages" row (js/core/footer.js renderFootLinks; the list is FOOT_LINKS in js/core/routes.js) and the Tier 2 to 4 slots ---------- */
.foot-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
  padding-block: 4px 12px;
  font-size: 0.875rem;
}
.foot-links a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding-inline: 2px;
}
.sv-xlinks {
  display: contents;
}
.ad-extras {
  display: grid;
  gap: 16px;
}
.ctl-state {
  color: var(--muted);
  font-weight: 500;
}
#ctl-pause-updates[aria-pressed='true'] {
  background: var(--gold-fill);
  border-color: var(--gold-fill);
  color: var(--on-gold);
}
#ctl-pause-updates[aria-pressed='true'] .ctl-state {
  color: inherit;
}

/* ---- accessibility pass (2026-10-15 audit) ----------------------------------------------------------------------------------------- */

/* WCAG 2.4.11: the sticky <th> row must not cover a focused control when Shift+Tab scrolls it up. scroll-padding-top (html) clears the pinned bar;
   this clears the header row of the table too (measured: 34 px tall, one line, nowrap). */
:root {
  --thead-h: 38px;
}
table.data tbody :is(a, button, input, select, textarea, summary, [tabindex]) {
  scroll-margin-top: calc(var(--thead-h) + 8px);
}

/* the forecast marker goes on its own line under the chip at phone widths, so the chips keep sharing a row without overflowing at 320 */
@media (max-width: 440px) {
  .nchip[data-forecast='1'] {
    grid-template-columns: auto minmax(8px, 1fr) auto;
    row-gap: 0;
  }
  .nchip[data-forecast='1'] .nchip-fc {
    grid-column: 1 / -1;
    justify-self: end;
    font-size: 10.5px;
    line-height: 1.1;
  }
}

/* WCAG 1.4.10 Reflow: at 320 CSS px nothing may force a sideways page scroll. The two chamber chips share a row and shrink; the nav wraps (all
   eight links visible, no hidden scroll strip) instead of scrolling out of sight. */
@media (max-width: 760px) {
  .needles {
    flex: 1 1 100%;
    min-width: 0;
  }
  .nchip {
    flex: 1 1 0;
    min-width: 0;
    grid-template-columns: auto minmax(12px, 1fr) auto;
    gap: 6px;
    padding-inline: 8px;
  }
  .nchip-val {
    min-width: 0;
  }
  .nchip[data-forecast='1'] {
    grid-template-columns: auto minmax(8px, 1fr) auto auto;
  }
  .nav {
    flex-wrap: wrap;
    overflow-x: visible;
  }
}

/* WCAG 2.5.8 Target Size (Minimum), desktop too: the poll-close state links and the Alerts key-race label were 14-17 x 20 px (the phone block above had the fix; fine pointers did not) */
.bd-pc-states a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
}
.al-kr-label {
  min-width: 24px;
}

/* ---- state outline icons (js/core/stateicon.js) ---------------------------------------------------------------------------- */
.st-icon { display: inline-block; width: 1.35em; height: 1.35em; vertical-align: -0.3em; margin-right: 0.35em; color: var(--muted); flex: none; }
.st-icon svg { width: 100%; height: 100%; display: block; overflow: visible; }
.st-icon path { fill: currentColor; stroke: none; }
.st-icon-lg { width: 2.4em; height: 2.4em; vertical-align: -0.65em; margin-right: 0.5em; }
.st-icon-D { color: var(--d-fill); }
.st-icon-R { color: var(--r-fill); }
@media (forced-colors: active) { .st-icon path { fill: CanvasText; } }
