/* The TDI look: what the mechanical colour pass (scripts/restyle-codemod.mjs) cannot do. Linked LAST, after every css/*.css, so these win ties
   with the module sheets without !important. Everything reads the hub tokens (css/tdi/tokens.css) and the dashboard's party/chrome tokens
   (app.css :root). Delete the rules it overrides from the module sheets whenever convenient; nothing here depends on them being gone. */

/* ---- type, links, focus ---------------------------------------------------------------------------------------------------- */
body {
  font: var(--text-base) / var(--leading-base) var(--font-sans);
}
h1,
h2 {
  font-weight: 700; /* the vendored Fraunces is one static weight (700); 650/750 would only be rounded to it */
}
a {
  color: var(--link);
}
a:hover {
  color: var(--text);
}
/* WCAG 1.4.1: a link inside running text is told apart by more than colour (axe link-in-text-block failed on 13 links on 6 routes) */
.view a:not([class]),
.muted a,
.crumbs a,
.kpi-sub a,
p a,
li > a:not([class]),
small a,
.nd-decide-foot a,
.tp-toprow > a,
a[data-k] {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---- header chrome ----------------------------------------------------------------------------------------------------------- */
.topbar,
.stickybar,
.nav-row,
.topbar-main {
  background: var(--panel);
}
.stickybar {
  border-bottom-color: var(--border);
}
.ticker {
  background: var(--sunken);
}
.sim-banner {
  background: var(--gold-fill);
  color: var(--on-gold); /* 8.2:1 dark, 8.3:1 light. Was red (#b3261e): red is the hub's "anti" colour and the party colour of the Republicans */
}
.nav a[aria-current='page'],
.nav a[aria-current='true'] {
  color: var(--gold);
  background: var(--bg);
  box-shadow: inset 0 -2px 0 var(--gold);
}
.nav-badge {
  background: var(--accent-fill);
  color: var(--on-gold);
}
.nchip,
.ticker-btn {
  border-color: var(--control-border);
}
.ticker-btn,
.tk {
  min-height: 24px; /* WCAG 2.5.8; was 20.7 and 18.1 px */
}
.ticker-btn {
  padding-block: 2px;
}
.pill[data-state='bad'] {
  border-color: var(--anti);
}
.pill-env {
  border-color: var(--gold);
}
.banner {
  border-color: var(--gold);
  color: var(--text);
}
.toast,
.map-tip,
.chart-tip {
  border-color: var(--border-strong);
  box-shadow: var(--shadow);
}
.dlg {
  border-radius: var(--radius-xl);
  border-color: var(--border-strong);
}
.dlg::backdrop {
  background: color-mix(in srgb, #000 55%, transparent);
}

/* the theme switch: the hub's, copied from its index.html (same markup, same data-theme-set hook) */
.theme-toggle {
  display: inline-flex;
  align-self: center;
  border: 1px solid var(--control-border);
  border-radius: var(--radius-md);
  /* no overflow:hidden: it clipped the keyboard focus ring on the outer sides (WCAG 2.4.7); the end buttons round their own corners instead */
}
.theme-toggle button:first-child {
  border-start-start-radius: calc(var(--radius-md) - 1px);
  border-end-start-radius: calc(var(--radius-md) - 1px);
}
.theme-toggle button:last-child {
  border-start-end-radius: calc(var(--radius-md) - 1px);
  border-end-end-radius: calc(var(--radius-md) - 1px);
}
.theme-toggle button:focus-visible {
  position: relative;
  z-index: 1;
}
.theme-toggle button {
  min-height: 24px;
  padding: 4px 10px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--faint);
  font: inherit;
  font-size: 11px;
  letter-spacing: 0.4px;
  cursor: pointer;
}
.theme-toggle button + button {
  border-left: 1px solid var(--border);
}
.theme-toggle button:hover {
  background: var(--panel2);
  color: var(--text);
}
.theme-toggle button[aria-pressed='true'] {
  background: var(--gold-fill);
  color: var(--on-gold);
  font-weight: 600;
}

/* under the hub's own bar (html[data-hub], set by hub-nav.js): the dashboard's first row becomes the slim section header */
html[data-hub] .topbar-inner {
  min-height: 44px;
  padding-block: 6px;
}
html[data-hub] .brand-name {
  font-size: var(--text-lg);
}
@media (max-width: 560px) {
  html[data-hub] .countdown,
  html[data-hub] .pill-env {
    display: none;
  }
  .sim-banner {
    font-size: var(--text-sm);
  }
}

/* ---- controls ---------------------------------------------------------------------------------------------------------------- */
.btn,
input[type='search'],
input[type='text'],
input[type='number'],
input[type='password'],
input[type='url'],
input[type='email'],
input[type='tel'],
input[type='date'],
input[type='datetime-local'],
select,
textarea {
  border-color: var(--control-border); /* the old --line was 1.3-1.5:1 on its surface (WCAG 1.4.11 asks 3:1) */
  border-radius: var(--radius-md);
}
.btn {
  min-height: 34px;
}
.btn-primary {
  background: var(--gold-fill);
  border-color: var(--gold-fill);
  color: var(--on-gold);
}
.btn-primary:hover {
  background: var(--gold-fill);
  filter: brightness(1.06);
}
/* a:hover above recolours the label to --text; on a link-styled button that put near-white on gold in the dark theme (1.89:1). Keep the button's own label colour. */
a.btn-primary:hover,
a.btn-primary:focus-visible {
  color: var(--on-gold);
}
.btn-danger {
  background: transparent;
  border-color: var(--anti);
  color: var(--anti-text);
}
/* app.css tints the hover 15% toward --bad, which dropped the red label to 4.14:1 on the dark card; 8% keeps >= 4.5:1 in both themes */
.btn-danger:hover {
  background: color-mix(in srgb, var(--anti) 8%, transparent);
}
a.btn-danger:hover,
a.btn-danger:focus-visible {
  color: var(--anti-text);
}
.seg-toggle {
  border-color: var(--control-border);
  border-radius: var(--radius-md);
}
.seg-toggle button {
  border-right-color: var(--border);
}
.seg-toggle button[aria-pressed='true'] {
  background: var(--gold-fill);
  color: var(--on-gold);
  font-weight: 600; /* pressed = the hub's theme-toggle look; was party blue */
}

/* ---- chips, badges, rating chips (the hub's status-chip: outlined pill, text in the status colour) ---------------------------- */
.chip {
  border-radius: var(--radius-pill);
  border-color: currentColor;
  background: transparent;
}
.chip-lean,
.chip-likely {
  border-color: var(--control-border);
}
.chip-projected {
  color: var(--gold);
  border-color: var(--gold);
  background: transparent;
}
.chip-final {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--on-gold); /* the hub's .status-chip.bill-enacted: a called/final race is the one solid chip */
}
.badge-projected {
  color: var(--gold);
  border: 1px solid var(--gold);
  background: transparent;
  font-weight: 700; /* outlined, not solid: a solid gold badge on every row of a 100-event feed is heavy (ui-spec section 1, item 5) */
}
.badge-status,
.badge-tipping,
.badge-forecast_miss,
.tag-warn {
  color: var(--gold);
  border-color: var(--gold);
  background: transparent;
}
.badge-lead_change,
.tag-demo {
  color: var(--spend);
  border-color: var(--spend);
}
.badge-bellwether {
  color: var(--orgs);
  border-color: var(--orgs);
}
.badge-first_votes {
  color: var(--pro);
  border-color: var(--pro);
}
/* Cook / Sabato / Inside ratings: outline only. Strength is shape, not tint (a 22-32% party tint under 11.5px text was 3.6-4.2:1 on the light
   panel): tilt and lean dashed, likely solid, solid doubled. The party is the text colour AND the word ("Lean D"). */
.rate,
.rate-D,
.rate-R,
.rate-likely.rate-D,
.rate-likely.rate-R,
.rate-solid.rate-D,
.rate-solid.rate-R {
  border-radius: var(--radius-pill);
  background: transparent;
  border: 1px solid currentColor;
}
.rate-D,
.rate-likely.rate-D,
.rate-solid.rate-D {
  color: var(--d);
}
.rate-R,
.rate-likely.rate-R,
.rate-solid.rate-R {
  color: var(--r);
}
.rate-toss {
  color: var(--text);
  border-color: var(--control-border);
}
.rate-tilt,
.rate-lean {
  border-style: dashed;
}
.rate-solid {
  box-shadow: inset 0 0 0 1px currentColor;
}
.rate i {
  color: var(--text);
}
.proj-base .m-D {
  color: var(--d);
}
.proj-base .m-R {
  color: var(--r);
}
.pty-I,
.pty-L,
.pty-G,
.pty-O {
  background: var(--grey);
}
.al-clock,
.mx-unit {
  color: var(--muted); /* --faint is only promised on bg/panel/panel2/sunken, not on a selected row or a tint */
}
.bd-pc-states a {
  display: inline-block;
  min-width: 24px;
  min-height: 24px;
  line-height: 24px;
  text-align: center;
}
.bd-pc-states a.has-key {
  font-weight: 700; /* was party blue: a state with a key race is not "Democratic" */
}
.tp-tags a {
  display: inline-block;
  min-height: 24px;
  line-height: 24px;
}

/* ---- tables and cards (the hub's .tdi-table / section) -------------------------------------------------------------------------- */
.card {
  border-radius: var(--radius-lg);
}
.data th {
  background: var(--panel2);
  color: var(--faint);
  font-size: var(--text-2xs);
  letter-spacing: 0.5px;
}
.race-row:hover td {
  background: var(--panel2);
}
.meter i {
  background: var(--mid);
}
@media (prefers-reduced-motion: no-preference) {
  .skeleton i {
    animation: shimmer 1.4s linear infinite;
  }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton i {
    animation: none; /* the shimmer ran regardless before (WCAG 2.2.2) */
  }
}
.empty,
.muted.empty {
  color: var(--muted);
}

/* ---- map ------------------------------------------------------------------------------------------------------------------------ */
.mx-map,
.mx-embed-host {
  background: var(--map-bg);
}
.mx-flat path {
  fill: var(--map-flat) !important;
  stroke: var(--map-edge);
}
.mx-backdrop.mx-layer path {
  fill: var(--map-flat) !important;
}
.mx-grp path,
.county-map path {
  stroke: var(--map-edge);
}
.mx-fill path.is-projected,
.mx-fill path.is-final {
  stroke: var(--proj-edge);
}
.mx-mark > circle {
  stroke: var(--proj-edge);
}
.mx-badge circle {
  stroke: var(--on-gold);
}
.mx-badge text {
  fill: var(--on-gold);
}
.mx-lg-proj {
  border-color: var(--proj-edge);
}
/* a label over a coloured fill stays white-with-a-dark-halo (legible on every fill in both themes); over a no-data / no-race area it is
   the page ink with a page-coloured halo. scene.js adds .is-flat (see the JS list). */
.mx-label.is-flat {
  fill: var(--text);
  stroke: var(--panel);
}
/* selection and hover are the only strokes that get a halo (a few overlay paths, so the filter is cheap) */
.mx-sel-state,
.mx-sel-district,
.mx-sel-county,
.mx-sel-hover {
  filter: drop-shadow(0 0 1.5px var(--panel));
}

/* ---- needle ----------------------------------------------------------------------------------------------------------------------- */
.nd-cone {
  fill: color-mix(in srgb, var(--text) 8%, transparent);
}
.nd-minor {
  stroke: color-mix(in srgb, var(--panel) 60%, transparent);
}
.nd-tag-live {
  border-color: var(--control-border);
}
.tp-livetag {
  color: var(--text);
  border-color: var(--control-border);
}

/* ---- target size (WCAG 2.5.8): the controls axe measured under 24px ----------------------------------------------- */
.ticker-label {
  display: inline-flex;
  align-items: center;
  min-width: 24px;
  min-height: 24px;
}
.ticker-label {
  position: relative;
  z-index: 1;
}
.mx-lg-proj {
  border-color: var(--proj-edge);
}

/* the "all" link in a map-panel heading: the heading is a baseline-aligned flex row, so a 24px target hung out of it and the next list covered its bottom edge */
.mx-h {
  align-items: center;
  min-height: 24px;
}
.mx-more {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
}

/* ---- dimmed rows: opacity is not a way to say "off" (it dropped disabled-source rows to 2.9-3.5:1; 49 axe failures on #/admin before this
   restyle too). Say it with the muted ink and italics instead. admin.css is injected after this file, so these use the same specificity
   plus the extra class admin.css's own rules do not have. */
.src-row.is-off td,
.src-row.is-off td *,
.ad-chain-list li.is-off,
.ad-chain-list li.is-off * {
  opacity: 1;
}
.src-row.is-off td,
.ad-chain-list li.is-off {
  color: var(--muted);
  font-style: italic;
}

/* tipping.css declares its three series colours on .tp-page (deeper than :root), so they are re-pointed here (or edit tipping.css lines 7-9) */
.tp-page {
  --tp-forecast: var(--spend);
  --tp-markets: var(--orgs);
  --tp-live: var(--text);
}

/* a tint under 11-13px text must stay light enough for the text on it: derive it from the text colour (9%). The codemod's 16-18% mixes of
   --ok measured 4.38-4.46:1 under the "On" switch on #/admin at phone width (the table becomes cards there). */
.switch.on,
.tip-done,
.mkp-live .mkp-tag,
.mkp-stale .mkp-tag {
  background: color-mix(in srgb, currentColor 9%, transparent);
}

/* failure is never colour alone: a glyph before the word (the pill text already says "failing"); empty alt text keeps it out of the accessible name */
.pill[data-state='bad'] .pill-text::before {
  content: '\2715\a0' / '';
}

/* ---- WCAG 1.4.12 Text Spacing / 1.4.4: names are never clipped with an ellipsis -----------------------------------------------------
   These single-line, ellipsised names grew past their box (and were cut off) once a reader applied the text-spacing minimums.
   They wrap instead; the layouts around them are flex/grid and take the extra height. Linked last so it beats the module sheets. */
.mx-row-sub,
.sv-cn,
.where-name,
.al-kr-name,
.al-kr-label,
.nd-dec-name,
.src-url,
.ad-url,
.al-chip > span,
.cand-name > span:nth-child(2),
.sv-eyebrow,
.mx-attrib {
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* ---- Windows High Contrast / forced colours (WCAG 1.4.11) ------------------------------------------------------------------------------
   Forced colours swaps every background for Canvas, which erased the seat bars, chamber chips, legend ramps, share bars and status dots (the text beside
   each still says the number, but the graphic was gone). These keep their authored fills (the party blues and reds read on the forced Canvas) and get a
   system-colour outline so the edge of the bar is always drawn. Text, links, buttons and borders everywhere else follow the user's palette. */
@media (forced-colors: active) {
  :is(.seg, .bop-bar, .bd-tally-bar, .nchip-bar, .lg-bar, .cand-bar, .nd-g-prog-bar, .nd-tally-bar, .nd-dec-bar, .nd-src-bar, .sv-bar, .tp-bar, .tp-pbar, .tp-sw, .rv-layerbar, .brand-mark,
      .meter, .meter2, .mx-bar-track, .mx-ctl-bar, .mx-fill, .bv-band, .k-band, .proj-band, .bandviz, .rv-cz-band, .nd-band, .mx-conn-dot, .poll-dot),
  :is(.bop-bar, .bop-segs, .bd-tally-bar, .nchip-bar, .cand-bar, .nd-tally-bar, .nd-dec-bar, .nd-src-bar, .nd-g-prog-bar, .sv-bar, .tp-bar, .tp-pbar, .brand-mark, .meter, .mx-bar-track, .mx-ctl-bar, .proj-band) > *,
  .lg-bins li,
  .lg-hatch,
  .mx-lg-proj,
  .mx-lg-none,
  .nd-refdot,
  .nd-hdot {
    forced-color-adjust: none;
  }
  /* the "no votes counted yet" hatch key keeps its pattern and gets an edge, so the key is still there when the fills are not */
  .lg-hatch,
  .mx-lg-proj,
  .mx-lg-none {
    outline: 1px solid CanvasText;
    outline-offset: -1px;
  }
  :is(.bop-bar, .bd-tally-bar, .nchip-bar, .lg-bar, .cand-bar, .nd-tally-bar, .nd-dec-bar, .nd-src-bar, .nd-g-prog-bar, .sv-bar, .tp-bar, .tp-pbar, .tp-sw, .brand-mark, .lg-bins li, .meter, .mx-bar-track, .proj-band) {
    outline: 1px solid CanvasText;
    outline-offset: -1px;
  }
  /* a status dot is a shape in the text colour (the pill's words carry the state: "50 sources OK", "2 of 50 sources failing") */
  .pill .dot {
    forced-color-adjust: none;
    background: CanvasText;
  }
  .pill[data-state='bad'] .dot {
    border-radius: 0; /* a square for "bad", a circle for the rest: a second cue that does not rely on colour */
  }
  /* every "selected" control is a fill in the author palette; forced colours drops fills, so the pressed / selected one is repainted with the system selection pair
     (and still says so in words through aria-pressed / aria-selected). Same pair as the theme switch above. */
  .theme-toggle button[aria-pressed='true'],
  .ctl[aria-pressed='true'],
  #ctl-pause-updates[aria-pressed='true'],
  .ticker.is-paused .ticker-pause,
  .seg-toggle button[aria-pressed='true'],
  .nd-chip[aria-pressed='true'],
  .nd-wobble[aria-pressed='true'],
  .ticker-mute[aria-pressed='true'],
  .rv-tabs [role='tab'][aria-selected='true'],
  .nd-suggest li.is-active,
  .rv-pick select.is-active {
    forced-color-adjust: none;
    background: Highlight;
    color: HighlightText;
    border-color: HighlightText;
  }
  /* current page in the top nav: a thick underline survives where the inset shadow does not */
  .nav a[aria-current='page'],
  .nav a[aria-current='true'] {
    text-decoration: underline;
    text-decoration-thickness: 3px;
    text-underline-offset: 6px;
  }
  /* selected rows (map panel, map table, history contests): a system-colour bar on the leading edge */
  .mx-row.is-sel,
  .mx-table tr.is-sel td:first-child,
  .rv-hcrow.is-sel td:first-child {
    forced-color-adjust: none;
    border-inline-start: 4px solid Highlight;
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
  /* the layer-attribution line sits in its own row now; keep its edge visible */
  .mx-attrib {
    border-top-color: CanvasText;
  }
  /* toasts get a real edge */
  .toast {
    border: 2px solid CanvasText;
  }
}

/* ---- the hub bar's search field (hub origin only) ------------------------------------------------------------------------------------------ */
/* app/tdi-nav.css is the HUB's stylesheet and gives #tdi-search-q a 1.2:1 boundary (light) and 1.3:1 (dark) against the page: WCAG 1.4.11 asks 3:1 for the edge of a field.
   This is a stopgap in our own sheet, only under the hub (hub-nav.js sets data-hub); the fix that belongs to the hub is border-color: var(--control-border) in tdi-nav.css,
   after which this rule can go. The id selector wins over the hub's class selector whatever order the sheets load in. */
html[data-hub='1'] #tdi-search-q {
  border-color: var(--control-border);
}
