:root {
  --paper: #f7f7f5;
  --card: #ffffff;
  --border: #dcdcd6;
  --ink-900: #1b1b18;
  --ink-700: #4a4a45;
  --ink-500: #7a7a73;
  --accent: #2f6f4f;
  --accent-ink: #ffffff;
  --green-600: #2f9e5c;
  --amber-500: #d69b1f;
  --red-600: #c0392b;
  --blue-500: #3170b8;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  --radius: 10px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #17181a;
    --card: #212226;
    --border: #34363b;
    --ink-900: #f1f1ef;
    --ink-700: #c7c7c2;
    --ink-500: #93938d;
    --accent: #4fae7d;
    --accent-ink: #0d130f;
    --blue-500: #6fb3ec;
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }
}

:root[data-theme="dark"] {
  --paper: #17181a;
  --card: #212226;
  --border: #34363b;
  --ink-900: #f1f1ef;
  --ink-700: #c7c7c2;
  --ink-500: #93938d;
  --accent: #4fae7d;
  --accent-ink: #0d130f;
  --blue-500: #6fb3ec;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink-900);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.4;
}

/* header + warning banner + nav.tabs stack as one unit and stick together as
   a single sticky element (rather than each independently sticky, which
   would overlap - see app.js's --sticky-top, which mirrors this block's
   actual rendered height for anything that needs to stick below it). */
.app-top {
  position: sticky;
  top: 0;
  z-index: 100;
}

header.app-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--card);
}

.header-meta {
  color: var(--ink-500);
  font-size: 12px;
  margin-left: auto;
  text-align: right;
}

select#league-select {
  padding: 6px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card);
  color: var(--ink-900);
}

.warning-banner {
  background: color-mix(in srgb, var(--amber-500) 18%, var(--card));
  border: 1px solid var(--amber-500);
  color: var(--ink-900);
  padding: 8px 16px;
  font-size: 12px;
}

nav.tabs {
  display: flex;
  gap: 4px;
  padding: 8px 16px 0;
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
  background: var(--card);
}

nav.tabs button {
  border: none;
  background: transparent;
  color: var(--ink-500);
  padding: 8px 14px;
  font-size: 13px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
}

nav.tabs button.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

.tabpanel { display: none; padding: 16px; max-width: 1100px; margin: 0 auto; }
.tabpanel.active { display: block; }

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px;
  margin: 0 auto 16px;
}

.card h2 { font-size: 14px; margin: 0 0 10px; }
.card h3 { font-size: 12px; color: var(--ink-500); margin: 12px 0 6px; text-transform: uppercase; letter-spacing: 0.04em; }

/* Some cards (Team Strength's viz/depth/pickups/targets, Start/Sit's lineup)
   read better narrower than the wide grid views next to them - capped and
   centered (via .card's own margin: 0 auto) independently of the whole
   tabpanel's width. */
.card-compact { max-width: 720px; }
.card-medium { max-width: 900px; }

.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }
th, td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
th { color: var(--ink-500); font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; cursor: pointer; }
tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }

.row-meta { margin-top: 2px; line-height: 1.2; }

/* Start/Sit's lineup/bench rows: ESPN headshot (or team logo for D/ST) to
   the left of the existing name/meta stack, sized to roughly match ESPN's
   own lineup view. object-fit:cover crops a non-square source to the
   circle instead of squishing it; the background shows through the
   transparent margin most headshot PNGs have around the subject. */
.player-cell { display: flex; align-items: center; gap: 8px; }
.player-photo { width: 34px; height: 34px; min-width: 34px; border-radius: 50%; object-fit: cover; background: color-mix(in srgb, var(--ink-500) 12%, transparent); }

/* Player modal header: the same photo, a little bigger, next to the
   name/rank line instead of inline within it. */
.player-modal-header { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.player-modal-header h2 { margin: 0; }
.player-modal-header p { margin: 2px 0 0; }
.player-photo-lg { width: 56px; height: 56px; min-width: 56px; border-radius: 50%; object-fit: cover; background: color-mix(in srgb, var(--ink-500) 12%, transparent); }

/* Start/Sit's Player/Score cells hold multi-line stacked content (name,
   team/ownership, kickoff+opponent) - letting that text wrap instead of
   forcing the site-wide "th, td { white-space: nowrap }" default is what
   keeps the table itself no wider than the screen, rather than just
   scrollable. table-wrap's overflow-x below is still the fallback for
   anything that doesn't fit even wrapped (e.g. a very long player name). */
#tab-startsit td { white-space: normal; }

/* The Rest-of-season grid needs more room than the site-wide 1100px tabpanel
   cap allows once every remaining week gets its own column - widen just this
   tab so a widescreen viewport doesn't have to scroll to see it. */
#tab-startsit.tabpanel { max-width: 1400px; }

/* Rankings' table (up to 15 columns with Watch/NMD once a team's selected)
   needs just over the site-wide 1100px cap to avoid horizontal scroll -
   same reasoning/pattern as Start/Sit above, with headroom for whatever a
   row's longest player name/rank happens to be. */
#tab-rankings.tabpanel { max-width: 1300px; }

/* Rest-of-season opponent grid: full-cell color fill (via .heat-cell) with
   the opponent centered above its rank, and the player-name column pinned to
   one line (overriding the general Start/Sit white-space:normal above, which
   is only meant for the multi-line stacked Starters/Bench rows). */
.ros-cell { padding: 4px 3px; min-width: 40px; }
.ros-opp { font-weight: 700; font-size: 11px; }
.ros-rank { font-size: 10px; opacity: 0.85; }
.ros-name { white-space: nowrap; }

/* Settings' field rows used a fixed 220px label + inline range/select/checkbox
   in one unbroken flex row - on a narrow phone that's simply wider than the
   screen, with nothing to scroll (it's not a table). Letting the row wrap
   moves the control(s) to their own line under the label instead. */
.settings-label { width: 220px; flex-shrink: 0; }
.settings-help { margin: -4px 0 10px 220px; }
.settings-row input[type="range"] { flex: 1; min-width: 60px; }

@media (max-width: 700px) {
  .settings-row { flex-wrap: wrap; }
  .settings-label { width: 100%; }
  .settings-help { margin: -2px 0 10px 0; }
}

/* Playoff seeding rows: a "Next division winner" checkbox plus a 4-deep
   tiebreak dropdown chain per seed - group the chain's selects tightly so
   they read as one ranked sequence, and give the checkbox label breathing
   room before it. */
.seeding-chain { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.seeding-checkbox { display: inline-flex; align-items: center; gap: 4px; margin-right: 10px; white-space: nowrap; }

/* Start/Sit rows: ".desktop-col" (Opp/ESPN-proj columns) and ".mobile-line"
   (the same info, stacked inside the Player/Score cells instead) are two
   renderings of the same data - the 700px breakpoint below picks one. */
.mobile-line { display: none; }

.pill {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
}

/* Opponent(+matchup rank) pills (colors.js's opponentCellHtml, used on
   Start/Sit and Rankings) - a fixed width regardless of team-code/rank
   length, with the text centered inside it, so a column of them lines up
   instead of each pill hugging its own text width. */
.opp-pill { min-width: 64px; text-align: center; }

.bar-row { display: flex; align-items: center; gap: 8px; margin: 4px 0; font-size: 12px; }
.bar-row-total { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }

/* FAAB estimate distribution: a min-max track with the p25-p75 box
   highlighted and a marker line at the (weighted-average) point estimate -
   the real spread of the comp-based method's own nearest neighbors, not a
   modeled confidence interval. */
/* A free-agent bye-week fill-in (Start/Sit, Schedule's expanded lineup) -
   deliberately loud, this player is NOT actually on your roster and the
   whole point is making that obvious at a glance. */
.streamed-row { background: color-mix(in srgb, var(--amber-500) 14%, transparent); }
.streamed-row:hover { background: color-mix(in srgb, var(--amber-500) 22%, transparent); }
.stream-badge { background: var(--amber-500); }

.faab-dist { margin: 10px 0; }
.faab-dist-track { position: relative; height: 10px; background: var(--border); border-radius: 5px; }
.faab-dist-iqr { position: absolute; top: 0; bottom: 0; background: color-mix(in srgb, var(--accent) 45%, transparent); border-radius: 5px; }
.faab-dist-marker { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--accent); transform: translateX(-1px); }
.faab-dist-labels { display: flex; justify-content: space-between; font-size: 11px; color: var(--ink-500); margin-top: 4px; font-variant-numeric: tabular-nums; }

/* Confidence-to-win slider - a second marker (blue, distinct from the
   accent-colored conditional_price marker) on the SAME track, moved live
   by the range input below it rather than a separately-scaled widget. */
.faab-dist-marker-confidence { background: var(--blue-500); }
.faab-confidence { margin-top: 12px; }
.faab-confidence-row { display: flex; justify-content: space-between; align-items: baseline; font-size: 12.5px; margin-bottom: 4px; }
.faab-confidence-bid { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--blue-500); }
.faab-confidence-slider { width: 100%; accent-color: var(--blue-500); }
.bar-label { width: 90px; flex-shrink: 0; color: var(--ink-700); }
.bar-track { flex: 1; height: 14px; background: var(--border); border-radius: 4px; overflow: hidden; position: relative; }
.bar-fill { height: 100%; background: var(--accent); }
.bar-value { width: 60px; text-align: right; flex-shrink: 0; font-variant-numeric: tabular-nums; }

.select-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 12px; }
.select-row select { padding: 6px 10px; border-radius: 8px; border: 1px solid var(--border); background: var(--card); color: var(--ink-900); }

/* Start/Sit's Team/Week row is much narrower than the (now centered,
   still fairly wide) card it sits in - center the controls within that
   width rather than leaving them flush left. */
#tab-startsit .select-row { justify-content: center; }

.player-picker { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.player-chip {
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 12px;
  cursor: pointer;
  background: var(--card);
  color: var(--ink-700);
}
.player-chip.selected { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.trade-result { display: flex; gap: 16px; flex-wrap: wrap; }
.trade-side { flex: 1; min-width: 220px; }
.trade-verdict { font-size: 16px; font-weight: 700; margin: 10px 0; }

.heat-cell { text-align: center; font-variant-numeric: tabular-nums; color: #111; border-radius: 4px; }

.muted { color: var(--ink-500); }
.small { font-size: 12px; }

.pos-tag {
  display: inline-block;
  min-width: 26px;
  text-align: center;
  padding: 1px 5px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}

.clickable-row { cursor: pointer; }

.watch-star { cursor: pointer; font-size: 15px; color: var(--ink-500); }
.watch-star.watched { color: #d6a000; }

/* Blue rather than accent green - accent is already the hover/selected/link
   color everywhere, so a green own-team wash blended into those. Blue reads
   as a distinct "this is you" signal in both light and dark (see --blue-500). */
.your-team-row { background: color-mix(in srgb, var(--blue-500) 16%, transparent); }
.your-team-row:hover { background: color-mix(in srgb, var(--blue-500) 24%, transparent); }

.selected-row { background: color-mix(in srgb, var(--accent) 16%, transparent); }

.sparkline { display: flex; align-items: flex-end; gap: 1px; height: 20px; width: 90px; }
.spark-bar { flex: 1; min-width: 2px; border-radius: 1px; }

.value-readout { font-variant-numeric: tabular-nums; min-width: 40px; display: inline-block; }
code { background: color-mix(in srgb, var(--ink-500) 15%, transparent); padding: 1px 5px; border-radius: 4px; font-size: 12px; }
#settings-save { padding: 6px 14px; border-radius: 8px; border: 1px solid var(--accent); background: var(--accent); color: var(--accent-ink); cursor: pointer; font-weight: 600; }

details { margin-top: 10px; }
details summary { cursor: pointer; color: var(--accent); }
details[open] summary { margin-bottom: 8px; }

.totals-row td { border-top: 2px solid var(--border); border-bottom: none; background: color-mix(in srgb, var(--accent) 5%, transparent); }

/* table-layout:fixed makes the <colgroup> widths authoritative regardless of
   cell content, so home/score/away line up the same way at every week. */
.schedule-table { table-layout: fixed; width: 100%; }
.schedule-cell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center; font-variant-numeric: tabular-nums; }
.week-divider td { background: color-mix(in srgb, var(--ink-500) 10%, transparent); font-weight: 600; font-size: 12px; text-align: left; }

/* Win-probability bar under an unplayed matchup's home/away row - home's
   share on the left, away's on the right (see schedule.js's winProbBar).
   Its own row, not squeezed into the existing name cells, so it reads as
   one bar spanning the whole matchup rather than two disconnected slivers. */
.winprob-row td { padding: 0 8px 8px; border-bottom: 1px solid var(--border); }
.winprob-bar { position: relative; height: 8px; border-radius: 4px; background: var(--paper); }
/* Always exactly half the track's width - see schedule.js's winProbBar for
   the geometry (a fixed-length indicator that SLIDES along the track,
   rather than a proportional two-color fill). */
.winprob-slider { position: absolute; top: 0; bottom: 0; width: 50%; display: flex; border-radius: 4px; overflow: hidden; }
.winprob-seg.winprob-home { background: var(--accent); }
.winprob-seg.winprob-away { background: color-mix(in srgb, var(--ink-500) 55%, transparent); }
/* Dead-center (50/50) reference tick - taller than the bar itself so it
   still reads as a landmark even when the slider fully covers the track
   (which it does whenever both teams have some real chance). */
.winprob-center-line { position: absolute; top: -2px; bottom: -2px; left: 50%; width: 1px; background: var(--ink-500); opacity: 0.6; }

/* Schedule's expanded matchup: one shared table mirrored around a single
   center "slot" column, home outside-left and away outside-right (players
   hugging the outer edges, scores hugging the middle), rather than two
   independent side-by-side tables. */
.lineup-symmetric-heading { display: flex; justify-content: space-between; gap: 8px; }
.lineup-symmetric-heading h3 { margin: 0 0 6px; }
.lineup-symmetric { width: 100%; table-layout: fixed; }
.lineup-player-home { text-align: right; }
.lineup-player-away { text-align: left; }
.lineup-score { text-align: center; font-variant-numeric: tabular-nums; }
.lineup-slot { text-align: center; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; }

/* :not([hidden]) matters here - a bare .modal-overlay{display:flex} rule
   ties in specificity with the UA's [hidden]{display:none} and, as the
   later/author rule, would win, leaving the modal visible after el.hidden
   is set to true. */
.modal-overlay:not([hidden]) {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 5vh 16px;
  z-index: 1000;
  overflow-y: auto;
}
.modal-box {
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 20px;
  max-width: 860px;
  width: 100%;
  position: relative;
}
.modal-close {
  position: absolute;
  top: 10px;
  right: 12px;
  border: none;
  background: transparent;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  color: var(--ink-500);
}
.modal-box h2 { font-size: 16px; margin: 0 0 4px; }

@media (max-width: 700px) {
  .tabpanel { padding: 10px; }
  /* The league select is the only item left in normal flow on mobile (it
     wraps to give the OPTIONS room, e.g. a long league name) - Week X /
     Updated tucks into the header's own top-right corner via absolute
     positioning instead of stacking below it, so it doesn't eat vertical
     space that's precious in the sticky header (--sticky-top eats into the
     visible page on a short mobile screen). padding-right on the header
     keeps a long select value from ever running under that corner text. */
  header.app-header { flex-direction: column; align-items: flex-start; position: relative; padding-right: 110px; }
  header.app-header select#league-select { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
  .header-meta { position: absolute; top: 12px; right: 16px; margin-left: 0; text-align: right; }
  th, td { padding: 5px 6px; font-size: 12px; }
  select, input, button { font-size: 16px; } /* prevents iOS auto-zoom on focus */
  nav.tabs button { padding: 8px 10px; font-size: 12px; }
  .modal-box { padding: 14px; max-width: 100%; }
  .bar-label { width: 64px; }
  .bar-value { width: 46px; }
  .player-picker { gap: 4px; }
  .desktop-col { display: none; }

  /* Start/Sit's Team/Week row: drop the label text (the selects' own values
     are self-explanatory - a manager name, a week number) and let the team
     select's owner name truncate with an ellipsis instead of pushing the
     Week select onto its own line, so both stay on one row. */
  #tab-startsit .select-row { flex-wrap: nowrap; }
  #tab-startsit .select-row label { display: none; }
  #tab-startsit .select-row select { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  #tab-startsit #startsit-team-select { flex: 3 1 0; }
  #tab-startsit #startsit-week-select { flex: 1 1 0; min-width: 64px; }
  .mobile-line { display: block; }
}

@media (pointer: coarse) {
  nav.tabs button, .player-chip, th, .modal-close { min-height: 30px; }
}

/* The filter row sticks below the frozen .app-top bar. --sticky-top
   (app.js) tracks .app-top's real height, since it can change (warning
   banner, filters wrapping on narrow screens). */
.rankings-filters {
  position: sticky;
  top: var(--sticky-top, 0px);
  z-index: 41;
  background: var(--card);
  padding: 6px 0;
  margin: 0 0 12px;
}
/* The table header can NOT use the same page-level sticky trick: any
   ancestor with overflow-x set (needed here so a 14+ column table can
   scroll horizontally on a narrow screen) forces its overflow-y to compute
   as auto too (CSS Overflow Module's visible/non-visible pairing rule) -
   that silently turns #rankings-table-wrap into the sticky th's containing
   block instead of the viewport, and since #rankings-table-wrap's own
   scrollTop never moves (the page scrolls, not the div), the th just sits
   at its static position and never visibly sticks. Confirmed empirically
   (Playwright: th tracked exactly 144px below the wrap's own top through
   1000px of page scroll - a page-relative offset of 0, not a bug in the
   `top` calc's arithmetic).
   Fix: stop fighting that pairing and use it instead - give the wrap its
   own bounded height and let IT scroll (both axes), so `top: 0` sticks the
   header to the top of its own real scrollport. 40px below accounts for
   .card's 14px top+bottom padding plus .rankings-filters' 12px bottom
   margin (not included in --rankings-filters-h, which is only its
   offsetHeight). border-collapse:separate is still required - position:
   sticky on a <th> is unreliable under the site-wide collapsed-border
   default in every major engine. */
#rankings-table-wrap table { border-collapse: separate; border-spacing: 0; }
#rankings-table-wrap {
  overflow: auto;
  max-height: calc(100vh - var(--sticky-top, 0px) - var(--rankings-filters-h, 0px) - 40px);
}
#rankings-table-wrap thead th {
  position: sticky;
  top: 0;
  background: var(--card);
  z-index: 2;
}

/* Compact stat summary at the top of the player modal - a grid of tiles
   instead of a narrow label/value column, so it uses the modal's full width. */
.player-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin: 12px 0;
}
.stat-tile {
  background: color-mix(in srgb, var(--ink-500) 8%, transparent);
  border-radius: 8px;
  padding: 8px 12px;
}
.stat-tile .stat-label { font-size: 11px; color: var(--ink-500); text-transform: uppercase; letter-spacing: 0.03em; }
.stat-tile .stat-value { font-size: 17px; font-weight: 600; margin-top: 2px; }

/* Player modal's own small tab bar (Overview / FAAB Bid) - visually related
   to the page-level nav.tabs but deliberately a separate, smaller class:
   this one lives inside a fixed-width .modal-box rather than a full-width
   sticky header, and only ever has 1-2 tabs. Only rendered at all when a
   FAAB estimate exists for the player (most players aren't on waivers) -
   otherwise the modal stays the single flat view it always was. */
.modal-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin: 14px 0 12px; }
.modal-tab-btn {
  border: none; background: transparent; color: var(--ink-500);
  padding: 8px 14px; font-size: 13px; font-weight: 500; cursor: pointer;
  border-bottom: 2px solid transparent;
}
.modal-tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.modal-tabpanel { display: none; }
.modal-tabpanel.active { display: block; }

/* FAAB signal chips - won/outbid/other_failure/no_bid, reusing the site's
   existing semantic colors (accent=good/won, amber=contested, red=failed,
   muted=no interest) rather than inventing a parallel palette. */
.sig-chip {
  display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 10px;
  font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; white-space: nowrap;
}
.sig-chip.sig-won { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.sig-chip.sig-outbid { background: color-mix(in srgb, var(--amber-500) 22%, transparent); color: var(--amber-500); }
.sig-chip.sig-other_failure { background: color-mix(in srgb, var(--red-600) 18%, transparent); color: var(--red-600); }
.sig-chip.sig-no_bid { background: color-mix(in srgb, var(--ink-500) 16%, transparent); color: var(--ink-500); }

/* Comp tables' rank/flags cells stack two related numbers/badges on top of
   each other rather than adding two more columns - keeps the table from
   growing wider than a phone screen can show without scrolling for
   information that reads fine stacked. */
.comp-rank-cell, .comp-recent-cell { line-height: 1.3; }
.comp-rank-cell .sub, .comp-recent-cell .sub { display: block; font-size: 10.5px; color: var(--ink-500); }
.comp-flag { display: inline-block; font-size: 10px; font-weight: 600; padding: 1px 5px; border-radius: 4px; margin-right: 3px; background: color-mix(in srgb, var(--red-600) 16%, transparent); color: var(--red-600); }
.comp-flag.tm { background: color-mix(in srgb, var(--amber-500) 18%, transparent); color: var(--amber-500); }

/* Per-owner FAAB interest levels (Low/Med/High) - a deliberately coarse
   bucket rather than a number: fa_values' real point-gain is only
   meaningful relative to this week's own gain distribution (see
   engine/pipeline.py's gain_level), so a raw figure would read as more
   precise than it actually is. High reuses the accent (a real, positive
   "this owner should want him" signal, not a warning), medium is amber,
   low fades to muted - a soft traffic-light read without literally using
   red for anything here (nothing in this list is bad news). */
.interest-chip { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
.interest-chip.high { background: color-mix(in srgb, var(--accent) 22%, transparent); color: var(--accent); }
.interest-chip.medium { background: color-mix(in srgb, var(--amber-500) 22%, transparent); color: var(--amber-500); }
.interest-chip.low { background: color-mix(in srgb, var(--ink-500) 16%, transparent); color: var(--ink-500); }
.handcuff-tag { font-size: 11px; font-weight: 600; color: var(--accent); }

/* Compare view - two full player-modal renders side by side (see
   playermodal.js's openComparePlayerModal). .modal-overlay-wide only
   applies while a wide modal is open (see modal.js's openModal(html,
   {wide}) - the plain single-player modal keeps its normal 860px cap. */
.modal-overlay-wide .modal-box { max-width: min(1500px, 96vw); }
.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.compare-col { padding-right: 4px; }
.compare-col[data-compare-col="a"] { border-right: 1px solid var(--border); padding-right: 20px; }
.compare-toggle { display: none; }

.compare-checkbox { margin-right: 6px; vertical-align: middle; cursor: pointer; }

/* Floating "N selected, ready to compare" tray - a fixed pill at the bottom
   of the viewport rather than anything tied to one tabpanel, since the
   selection itself (compare.js) persists across Start/Sit <-> Rankings. */
.compare-tray {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px;
  background: var(--card); border: 1px solid var(--border); border-radius: 999px;
  box-shadow: var(--shadow); padding: 8px 10px 8px 16px; z-index: 500; max-width: 90vw;
}
.compare-tray-label { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.compare-tray-go {
  border: none; border-radius: 999px; padding: 6px 14px; font-size: 12.5px; font-weight: 600; cursor: pointer;
  background: var(--accent); color: var(--accent-ink); white-space: nowrap;
}
.compare-tray-go:disabled { background: color-mix(in srgb, var(--ink-500) 25%, transparent); color: var(--ink-500); cursor: default; }
.compare-tray-clear { border: none; background: transparent; color: var(--ink-500); font-size: 18px; line-height: 1; cursor: pointer; padding: 0 2px; }

@media (max-width: 700px) {
  .modal-tab-btn { padding: 8px 10px; font-size: 12px; }
  .modal-overlay-wide .modal-box { max-width: 100%; padding: 14px; }
  .compare-toggle { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 12px; }
  .compare-side-btn {
    flex: 1; border: none; background: transparent; color: var(--ink-500); padding: 8px 6px; font-size: 12.5px;
    font-weight: 500; cursor: pointer; border-bottom: 2px solid transparent; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .compare-side-btn.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
  .compare-grid { display: block; }
  .compare-col { display: none; padding-right: 0 !important; border-right: none !important; }
  .compare-col.active { display: block; }
  .compare-tray { bottom: 10px; padding: 6px 8px 6px 12px; }
  .compare-tray-label { max-width: 40vw; }
}
@media (pointer: coarse) {
  .modal-tab-btn { min-height: 30px; }
  .compare-checkbox { width: 18px; height: 18px; }
}
