/* Design tokens (colors, fonts, radii, shadows) live in theme.css, loaded before this
   file — everything below is layout/structure only, built entirely on var(--...) tokens
   so reskinning never requires touching this file. */

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-body);
  background:
    radial-gradient(circle at 15% 0%, var(--glow-1) 0%, transparent 45%),
    radial-gradient(circle at 100% 10%, var(--glow-2) 0%, transparent 40%),
    var(--bg);
  color: var(--text);
  min-height: 100vh;
  transition: background-color 0.15s ease, color 0.15s ease;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Frosted-glass sticky nav bar, iOS-style: translucent + blurred rather than a flat opaque
   panel, so page content is still faintly readable sliding underneath it on scroll. */
.app-header {
  background: var(--header-bg-translucent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.header-inner {
  max-width: 1300px;
  margin: 0 auto;
  padding: 18px 24px 0;
}

.header-top-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.app-header h1 {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 10px;
  letter-spacing: -0.01em;
}
.app-header h1 em {
  font-style: normal;
  color: var(--accent);
}

.theme-toggle-btn {
  background: var(--surface-muted);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 999px;
  width: 40px;
  height: 40px;
  padding: 0;
  font-size: 1.1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.theme-toggle-btn:hover { background: var(--surface-muted-hover); }

/* iOS segmented control: a rounded pill-shaped track holding the tab buttons, with the active
   one reading as a distinct floating segment (its own background + soft shadow) rather than an
   underline — the same pattern iOS uses for Photos' Library/For You/Albums switcher, etc. */
.tabs {
  display: flex;
  gap: 2px;
  overflow-x: auto;
  background: var(--surface-muted);
  padding: 3px;
  border-radius: 12px;
}

.tab-btn {
  background: none;
  border: none;
  padding: 8px 16px;
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 600;
  cursor: pointer;
  color: var(--muted);
  border-radius: 9px;
  white-space: nowrap;
  transition: color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}

.tab-btn:hover { color: var(--text); }

.tab-btn.active {
  color: var(--primary-dark);
  background: var(--panel-bg);
  box-shadow: var(--shadow-sm);
}

main {
  max-width: 1300px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}

.tab-panel { display: none; }
.tab-panel.active { display: block; animation: fadeIn 0.2s ease; }

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Two-column layout (main content + a standings sidebar) for the Games and Leaderboard tabs —
   both have grown into a long single-column stack of panels; a sticky sidebar gives an
   always-visible "how's everyone doing" reference without scrolling back up to it. Collapsible
   (toggleTabSidebar() in app.js) since the sidebar is a nice-to-have, not essential — the toggle
   strip stays visible either way so it's never a dead end. */
.tab-body-with-sidebar {
  display: grid;
  grid-template-columns: 1fr 260px;
  gap: 20px;
  align-items: start;
}
.tab-body-with-sidebar.sidebar-collapsed {
  grid-template-columns: 1fr auto;
}
.tab-main { min-width: 0; }
/* Sticky, but capped to the viewport height so a tall stack of sidebar cards (Standings +
   Recent Games, or a future third card) can never end up partly below the fold with no way to
   reach it — a sticky element doesn't shrink to fit on its own, it just pins its top edge and lets
   its bottom run off-screen on a short window. overflow-y:auto here means the sidebar scrolls
   independently of the page once it's taller than the space available under the sticky offset. */
.tab-sidebar {
  position: sticky;
  top: 84px;
  max-height: calc(100vh - 104px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.sidebar-toggle-btn {
  /* Sticky within .tab-sidebar's own internal scroll (not just the page), so it's still reachable
     without scrolling back up when the card stack below it is taller than the viewport. */
  position: sticky;
  top: 0;
  z-index: 1;
  align-self: flex-end;
  background: var(--bg);
  color: var(--muted);
  border-radius: 999px;
  width: 30px;
  height: 30px;
  padding: 0;
  font-size: 0.9rem;
  flex-shrink: 0;
}
.sidebar-toggle-btn:hover { background: var(--surface-muted-hover); }
.tab-body-with-sidebar.sidebar-collapsed .tab-sidebar-inner { display: none; }
@media (max-width: 900px) {
  .tab-body-with-sidebar, .tab-body-with-sidebar.sidebar-collapsed { grid-template-columns: 1fr; }
  /* Stacked below the main content instead of pinned beside it, so the viewport-relative
     max-height/scroll above (needed only to keep a sticky sidebar's bottom reachable) would just
     get in the way here — let it flow at its natural height like everything else on the page. */
  .tab-sidebar { position: static; max-height: none; overflow-y: visible; }
}

/* Compact standings widget (renderStandingsSidebar() in app.js) — tighter than the main
   Leaderboard table on purpose, this is an at-a-glance reference, not a full stat line. */
.standings-mini-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
}
.standings-mini-table th {
  text-align: left;
  padding: 4px 6px;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  white-space: nowrap;
}
.standings-mini-table td {
  padding: 5px 6px;
  border-top: 1px solid var(--surface-muted);
}
.standings-mini-table tr:first-child td { border-top: none; }
.standings-mini-player-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  color: var(--text);
  font-weight: 600;
  padding: 0;
}
.standings-mini-player-btn:hover { background: none; color: var(--accent); }
.standings-mini-player-btn .player-avatar { width: 18px; height: 18px; font-size: 0.62rem; }

/* Games sidebar's second card — last 5 games (renderGamesSidebarRecent() in app.js), each row a
   full-width button so the whole row is clickable, not just the text. */
.sidebar-recent-games-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.sidebar-recent-games-list li + li { border-top: 1px solid var(--surface-muted); }
.sidebar-recent-game-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  padding: 7px 2px;
  font-size: 0.8rem;
  color: var(--text);
  font-weight: 500;
}
.sidebar-recent-game-btn:hover { color: var(--accent); }
.sidebar-recent-game-score { font-weight: 700; font-variant-numeric: tabular-nums; }
.sidebar-recent-game-unreviewed { color: var(--muted); font-size: 0.72rem; font-style: italic; }

/* Leaderboard sidebar's "highlight" cards (renderLeaderboardHighlights() in app.js) — one stat
   leader per card instead of the full standings table Games' sidebar uses, since that would just
   repeat the Season Rates table sitting right next to it. */
.sidebar-highlight-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  background: var(--surface-tint);
}
.sidebar-highlight-card + .sidebar-highlight-card { margin-top: 8px; }
.sidebar-highlight-label {
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  margin-bottom: 6px;
}
.sidebar-highlight-card .standings-mini-player-btn { font-size: 0.85rem; }
.sidebar-highlight-card .standings-mini-player-btn .player-avatar { width: 20px; height: 20px; font-size: 0.68rem; }
.sidebar-highlight-detail {
  font-size: 0.72rem;
  color: var(--muted);
  margin-top: 4px;
  line-height: 1.35;
}

.panel {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 20px 24px;
  margin-bottom: 20px;
  box-shadow: var(--shadow-sm);
}

/* Groups two or more narrow panels (a small chart, a compact table) side by side instead of each
   one eating a full row's width and leaving the rest as dead space — wraps to a stacked column on
   narrow viewports same as everything else. The row itself carries the bottom margin so its own
   .panel children don't double up on spacing. */
.panel-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 20px;
  margin-bottom: 20px;
}
.panel-row > .panel {
  flex: 1 1 320px;
  margin-bottom: 0;
  min-width: 0;
}

.panel h2 {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
}

/* Most Leaderboard panels are <details>/<summary> instead of a plain div (see index.html) --
   the tab was ~20 panels stacked full-length, a lot of blind scrolling just to find one thing,
   worse on a phone. Season Rates (the primary table) stays open by default; everything else
   starts collapsed and opens on tap. The heading itself lives inside <summary> so the whole
   header row is the tap target, not just a separate toggle control next to it. */
.panel > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 0 0 10px;
}
.panel > summary::-webkit-details-marker { display: none; }
.panel > summary h2 { margin: 0; }
.panel > summary::after {
  content: "▸";
  color: var(--muted);
  font-size: 0.85rem;
  flex-shrink: 0;
  transition: transform 0.15s ease;
}
.panel[open] > summary::after { transform: rotate(90deg); }
.panel:not([open]) > summary { margin-bottom: 0; }
.panel > summary:hover h2 { color: var(--accent); }

.panel h4 {
  margin: 14px 0 6px;
  font-family: var(--font-display);
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.panel h4:first-of-type { margin-top: 0; }

.hint {
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.5;
  margin-top: -4px;
  margin-bottom: 12px;
}

/* Every panel's explanatory paragraph gets wrapped in one of these at runtime
   (collapseSectionHints() in app.js) so a page full of panels isn't a wall of text by default —
   collapsed to start, expand per-panel as needed. */
.hint-details {
  margin-top: -4px;
  margin-bottom: 12px;
}
.hint-details summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 600;
  list-style: none;
  padding: 2px 0;
  user-select: none;
}
.hint-details summary::-webkit-details-marker { display: none; }
.hint-details summary::before {
  content: "▸ About this panel";
}
.hint-details[open] summary::before {
  content: "▾ About this panel";
}
.hint-details .hint {
  margin-top: 6px;
  margin-bottom: 4px;
}

.inline-form {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.reel-export-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
  margin-top: 10px;
}

.leaderboard-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 16px;
  margin: 10px 0;
}

/* iOS-style toggle switch — a settings-row label paired with a track/knob, replacing a button
   whose own text used to flip between "Include X"/"Exclude X" to show state. The label now names
   the setting once; the switch position (and green fill when on) shows whether it's active,
   the same split every iOS Settings row uses. Built on a real (visually hidden) checkbox input
   so it's keyboard-operable and gets a native :checked state for free — the visible track/knob
   are sibling spans styled off that input's state via `:checked +`/`:disabled +`. */
.ios-switch-row {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  user-select: none;
}
.ios-switch {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 26px;
  flex-shrink: 0;
}
.ios-switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
  z-index: 1;
}
.ios-switch-track {
  position: absolute;
  inset: 0;
  background: var(--disabled-bg);
  border-radius: 999px;
  transition: background 0.2s ease;
}
.ios-switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 22px;
  height: 22px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s ease;
}
.ios-switch input:checked + .ios-switch-track { background: var(--success); }
.ios-switch input:checked + .ios-switch-track .ios-switch-knob { transform: translateX(18px); }
.ios-switch input:focus-visible + .ios-switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.ios-switch input:disabled { cursor: not-allowed; }
.ios-switch input:disabled + .ios-switch-track { opacity: 0.5; }
.ios-switch-row:has(input:disabled) { opacity: 0.6; cursor: not-allowed; }

/* Small live preview during the league-wide combine-video export — visible so there's some
   confirmation something's actually happening during what can be a long real-time recording,
   not full-size since it's not meant to be watched closely, just glanced at for progress. */
.league-export-preview {
  display: block;
  width: 220px;
  max-width: 100%;
  border-radius: var(--radius-sm);
  margin-top: 10px;
  background: #000;
}

input, select, button {
  font-family: var(--font-body);
  font-size: 0.9rem;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--panel-bg);
  color: var(--text);
}

/* Filled fields, not outlined boxes — the iOS text-field convention (Settings, Mail search:
   a soft gray capsule with no visible border until it's actually focused). */
input, select {
  background: var(--surface-muted);
  border-color: transparent;
  transition: background 0.15s ease, border-color 0.15s ease;
}
input:focus, select:focus {
  background: var(--panel-bg);
  border-color: var(--accent);
}

input:focus, select:focus, button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

button {
  background: var(--primary);
  color: white;
  border: none;
  cursor: pointer;
  font-weight: 600;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s ease, transform 0.1s ease;
}

button:hover { background: var(--primary-dark); }
button:active { transform: scale(0.96); }

.secondary-btn {
  background: var(--surface-muted);
  color: var(--text);
}
.secondary-btn:hover { background: var(--surface-muted-hover); }

.danger-btn {
  background: var(--danger);
}
.danger-btn:hover { filter: brightness(0.88); }

.highlight-btn {
  background: var(--success);
}
.highlight-btn:hover { filter: brightness(0.88); }

.lowlight-btn {
  background: var(--danger);
}
.lowlight-btn:hover { filter: brightness(0.88); }

button:disabled {
  background: var(--disabled-bg) !important;
  color: var(--muted);
  cursor: not-allowed;
  filter: none;
}

.button-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.file-label {
  display: inline-flex;
  align-items: center;
  padding: 9px 14px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-weight: 600;
  font-size: 0.9rem;
}

.games-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.roster-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 9px 2px;
  border-top: 1px solid var(--surface-muted);
  font-weight: 500;
}

.roster-row:first-child { border-top: none; }

.icon-btn {
  background: none;
  color: var(--muted);
  padding: 3px 8px;
  font-weight: 500;
  font-size: 0.82rem;
  border: none;
}
.icon-btn:hover { color: var(--danger); background: none; }

.game-card {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.game-card:hover {
  border-color: var(--accent);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.game-card .matchup-line {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.05rem;
}
.game-card .date-line {
  color: var(--muted);
  font-size: 0.84rem;
  margin-top: 2px;
}

.stat-entry-header {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.stat-entry-header h2 { margin: 0; flex: 1; font-family: var(--font-display); }

.score-display {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.3rem;
  color: var(--primary-dark);
  background: var(--primary-light);
  padding: 6px 14px;
  border-radius: 999px;
}

.scoreboard-panel {
  background: linear-gradient(135deg, var(--primary), var(--accent));
  padding: 0;
  overflow: hidden;
}

.scoreboard {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(16px, 5vw, 48px);
  padding: 20px 16px;
}

.scoreboard-team {
  display: flex;
  align-items: baseline;
  gap: 12px;
  opacity: 0.72;
  transition: opacity 0.15s ease;
}
.scoreboard-team.leading { opacity: 1; }

.scoreboard-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.95rem;
  color: white;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.scoreboard-value {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(2.4rem, 8vw, 3.5rem);
  line-height: 1;
  color: white;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.scoreboard-dash {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.8rem;
  color: rgba(255, 255, 255, 0.6);
}

.box-score-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

@media (max-width: 800px) {
  .box-score-columns { grid-template-columns: 1fr; }
}

.team-box {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: var(--shadow-sm);
}

.team-box h3 {
  margin-top: 0;
  margin-bottom: 12px;
  font-family: var(--font-display);
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 8px;
  border-bottom: 2px solid var(--primary-light);
}

.player-stat-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 10px;
  background: var(--surface-tint);
}

.player-stat-card .name-row {
  display: flex;
  justify-content: space-between;
  font-weight: 700;
  margin-bottom: 8px;
  font-family: var(--font-display);
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
  gap: 6px;
}

.stat-cell {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 6px 6px;
  text-align: center;
  background: var(--panel-bg);
}

.stat-cell .stat-label {
  font-size: 0.68rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-weight: 700;
}

.stat-cell .stat-value {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  margin: 3px 0;
}

.stat-cell .stat-buttons {
  display: flex;
  gap: 4px;
  justify-content: center;
}

.stat-cell button {
  /* Was 3px 7px (~20px tall) — the single most-tapped interaction in the app, and the smallest
     touch target in it. Bumped as far as the existing 4-across (PTS) / 3-across (MISS) row
     layout allows without widening .stat-cell itself and disrupting how many player cards fit
     per screen — still short of the ~44px ideal, but a real improvement, and flex-shrink on
     these buttons already lets them compress further on a genuinely narrow cell rather than
     overflow it. */
  padding: 7px 6px;
  font-size: 0.75rem;
  min-width: 26px;
  border-radius: 6px;
}

.matchup-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 10px;
}

.matchup-table th {
  text-align: left;
  padding: 8px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  border-bottom: 2px solid var(--border);
  white-space: nowrap;
}

.sortable-th {
  cursor: pointer;
  user-select: none;
}
.sortable-th:hover { color: var(--text); }
.sortable-th.sorted { color: var(--accent); }

.matchup-table td {
  text-align: left;
  padding: 8px;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}

/* Leaderboard-only: every non-player column is a stat, so give it the tabular-precision
   treatment — a monospace face keeps a column of numbers visually aligned in a way the body
   font can't (Leaderboard's own numbers are proportionally spaced without it). */
.matchup-table td.num-cell {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.matchup-table tr:hover td {
  background: var(--surface-hover);
}

/* Pins the player-name column in place while a wide stat table (Game Stats, Leaderboard)
   scrolls horizontally, so it's always clear whose row you're looking at. Needs its own solid
   background — the table itself has none, it just shows the panel behind it — or scrolled-past
   cells would show through underneath. */
.matchup-table th.sticky-col,
.matchup-table td.sticky-col {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--panel-bg);
  box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.15);
}
.matchup-table tr:hover td.sticky-col {
  background: var(--surface-hover);
}

/* On a narrow screen, a sticky first column with a long header (like the Head-to-Head grid's
   "Scorer / Defender") claims so much of the viewport's own width that only one or two data
   columns are ever visible beside it, and every other column is a further horizontal scroll away.
   Letting just the sticky header wrap onto multiple lines, and capping its width, gives the rest
   of the table room to actually show side by side. */
@media (max-width: 520px) {
  .matchup-table th.sticky-col {
    white-space: normal;
    max-width: 84px;
    line-height: 1.2;
  }
}

.empty-state {
  color: var(--muted);
  font-style: italic;
  padding: 12px 0;
}

/* Players roster */
.roster-row-name {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

/* Colored initial avatar (renderPlayerAvatar() in app.js) — background color is set inline
   per-player (hue derived from their id), only the shape/size/text styling lives here. */
.player-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: 50%;
  color: white;
  font-weight: 700;
  line-height: 1;
  /* When this is an <img> (a real photo) rather than a colored-initial <span> */
  object-fit: cover;
  background: var(--surface-muted);
}
.player-avatar-normal {
  width: 24px;
  height: 24px;
  font-size: 0.75rem;
}
.player-avatar-large {
  width: 52px;
  height: 52px;
  font-size: 1.5rem;
}
.player-name-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}
#playerDetailTitle {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Season-leader cell in the main Leaderboard table (renderLeaderboard() in app.js) — same
   green-for-better language as Player Comparison's .compare-better, just as a background tint
   since a whole table column is a busier context than two side-by-side numbers. */
.leaderboard-leader-cell {
  background: var(--success-light);
  color: var(--success);
  font-weight: 700;
  border-radius: var(--radius-sm);
}
.leaderboard-worst-cell {
  background: var(--danger-light);
  color: var(--danger);
  font-weight: 700;
  border-radius: var(--radius-sm);
}
.profile-tags {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
}
.profile-tag {
  /* Default = role-player / no specific hue below: the same neutral surface/muted pairing used
     elsewhere on the page, since "role-player" is the catch-all "nothing stands out" bucket, not
     a fourth real specialization deserving its own color. */
  background: var(--surface-tint);
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 8px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.profile-tag-scorer { background: var(--tag-scorer-bg); color: var(--tag-scorer-text); border-color: transparent; }
.profile-tag-defender { background: var(--tag-defender-bg); color: var(--tag-defender-text); border-color: transparent; }
.profile-tag-physical { background: var(--tag-physical-bg); color: var(--tag-physical-text); border-color: transparent; }
.profile-tag-playmaker { background: var(--tag-playmaker-bg); color: var(--tag-playmaker-text); border-color: transparent; }

/* Inline "Edit Tags" form (Players tab) — appended as its own row right under the player it
   edits, not a modal, so it stays anchored to the roster list it's changing. */
.physical-profile-editor {
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 4px 0 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.physical-profile-editor-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: flex-end;
}
.physical-profile-editor-row label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--muted);
}
.physical-profile-height-inputs {
  display: flex;
  align-items: center;
  gap: 4px;
  font-weight: 400;
}
.physical-profile-height-inputs input { width: 52px; }
.physical-profile-note-label { flex: 1; min-width: 220px; }
.physical-profile-note-label input { width: 100%; }
.physical-profile-roles {
  gap: 12px;
}
.physical-profile-role-check {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 5px;
  font-weight: 500;
  color: var(--text);
  font-size: 0.85rem;
}
.physical-profile-role-check input { width: auto; }
.physical-profile-editor-actions {
  display: flex;
  gap: 8px;
}

/* Roster assignment per game */
.roster-assignment {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}
@media (max-width: 800px) {
  .roster-assignment { grid-template-columns: 1fr; }
}
.roster-team-col {
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
}
.roster-team-col h4 {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 0.95rem;
}
.roster-chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-height: 30px;
  margin-bottom: 10px;
}
.roster-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--primary-light);
  color: var(--primary-dark);
  padding: 5px 6px 5px 12px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.85rem;
}
.roster-chip button {
  background: none;
  border: none;
  color: inherit;
  padding: 0 4px;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.65;
}
.roster-chip button:hover { opacity: 1; background: none; }
.roster-add-select {
  width: 100%;
}

/* Balance Teams (Games tab) — an attendee-picker chip row, a team-size control, and a shortlist
   of candidate splits, each rendered as a row of small team cards. */
.attendee-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}
.attendee-chip {
  /* Matches the app's own baseline button height (input/select/button's 9px 12px padding,
     ~34px tall) rather than the smaller pill this started as — a row of these is the main
     touch interaction on Balance Teams, and undersized tap targets are the first thing that
     goes wrong picking attendees on a phone at the court. */
  padding: 9px 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-tint);
  color: var(--text);
  font-weight: 600;
  font-size: 0.85rem;
  cursor: pointer;
}
.attendee-chip.selected {
  background: var(--primary-light);
  border-color: var(--primary-dark);
  color: var(--primary-dark);
}

/* Players tab role filter — reuses .profile-tag-<kind>'s own colors (so a chip's color always
   matches the tag it filters for) but sized as a real tap target, dimmed until picked so an
   all-off "showing everyone" state doesn't read as five different active filters. */
.role-filter-chip {
  cursor: pointer;
  padding: 6px 14px;
  font-size: 0.78rem;
  opacity: 0.5;
  transition: opacity 0.15s;
}
.role-filter-chip:hover { opacity: 0.75; }
.role-filter-chip.active {
  opacity: 1;
  box-shadow: 0 0 0 2px var(--primary-dark) inset;
}
.balance-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.balance-controls input[type="number"] {
  width: 60px;
  margin-left: 6px;
}
.balance-option {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 12px;
}
.balance-option-best {
  border-color: var(--success);
  background: var(--success-light);
}
.balance-option-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 10px;
}
.balance-spread {
  font-size: 0.85rem;
  color: var(--muted);
}
.balance-teams-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 10px;
}
.balance-team-card {
  background: var(--surface-tint);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  min-width: 150px;
}
.balance-team-card h5 {
  margin: 0 0 6px;
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.85rem;
}
.balance-team-metrics {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}
.balance-team-avg {
  color: var(--muted);
  font-weight: 500;
}
/* The model-predicted win% (see getWinProbabilityModel() in app.js) gets the accent color rather
   than the same muted tone as the plain average next to it — it's a prediction, not a measured
   fact like the avg is, and the color difference keeps that distinction visible at a glance. */
.balance-team-winprob {
  color: var(--accent);
  font-weight: 600;
}
/* Below WIN_PROBABILITY_CONFIDENCE_GAMES (see app.js), the prediction is already hedged toward
   50/50 internally — this is the visible half of that, so it doesn't read as more settled than
   it is: faded and italic rather than the same solid accent color a fully-trusted prediction
   gets. */
.balance-team-winprob-low-confidence {
  opacity: 0.65;
  font-style: italic;
  font-weight: 500;
}
.balance-team-card ul {
  margin: 0;
  padding-left: 18px;
}
.balance-team-physical {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  font-size: 0.78rem;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.balance-preview-wrap {
  margin-top: 10px;
}
.balance-preview-table {
  font-size: 0.85rem;
}

/* Player Comparison (Leaderboard tab) */
.compare-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.compare-controls select {
  flex: 1;
  min-width: 160px;
}
.compare-table td {
  text-align: center;
}
.compare-stat-label {
  text-align: left !important;
  color: var(--muted);
  font-weight: 600;
}
.compare-better {
  color: var(--success);
  font-weight: 700;
}
.compare-worse {
  color: var(--danger);
}

/* Games tab — Advanced Filters (collapsed by default). The [hidden] rule below is required, not
   redundant with the browser's own default — an author stylesheet's ".games-advanced-filters {
   display: flex }" outranks the UA stylesheet's own "[hidden] { display: none }" (author styles
   always beat UA styles), so without this the hidden attribute was toggling correctly in app.js
   but the panel stayed visibly on screen regardless, making the toggle button look like it did
   nothing. */
.games-advanced-filters {
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 14px 16px;
  margin-bottom: 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.games-advanced-filters[hidden] {
  display: none;
}
.filter-block-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
  font-weight: 700;
  margin-bottom: 6px;
}
.filter-inline-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  margin-right: 14px;
  margin-top: 8px;
}
.filter-stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* Stat Entry: the video and the box score (the two things you're actually looking at and
   clicking while tagging a play) sit side by side at the top, each tall enough to hold its
   own scroll if it needs one, so neither ever has to be scrolled away to see the other.
   Everything else — video setup, roster assignment, Game Stats, Shot Log, etc. — is used far
   less often while actively tagging, so it lives below in normal document flow instead.
   Falls back to a plain stacked column on narrow screens, where holding both at a fixed
   height would just cramp everything. */
.tagging-area {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: 18px;
  align-items: start;
  margin-bottom: 18px;
}
.video-pane {
  background: var(--panel-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px;
  box-shadow: var(--shadow-sm);
}
.stat-pane {
  max-height: 78vh;
  overflow-y: auto;
  padding-right: 4px;
}
.stat-pane .box-score-columns {
  grid-template-columns: 1fr;
  margin-bottom: 0;
}
@media (max-width: 900px) {
  .tagging-area { display: block; }
  .stat-pane { max-height: none; overflow-y: visible; margin-top: 18px; }
}

/* Video */
#videoPlayerWrap video,
#videoPlayerWrap iframe {
  width: 100%;
  max-height: 74vh;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: black;
  box-shadow: var(--shadow-md);
}
#videoPlayerWrap iframe {
  aspect-ratio: 16 / 9;
  max-height: none;
}

/* Thin overlay scrollbar, iOS-style — a slim translucent thumb that doesn't compete visually
   with the content, rather than the OS's default chunky bar. Firefox gets the closest built-in
   equivalent (scrollbar-width/-color); WebKit/Blink get the full treatment below. */
.table-scroll {
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--muted) transparent;
}
.table-scroll::-webkit-scrollbar, .tab-sidebar::-webkit-scrollbar { height: 6px; width: 6px; }
.table-scroll::-webkit-scrollbar-track, .tab-sidebar::-webkit-scrollbar-track { background: transparent; }
.table-scroll::-webkit-scrollbar-thumb, .tab-sidebar::-webkit-scrollbar-thumb {
  background: var(--muted);
  border-radius: 999px;
  opacity: 0.5;
}
.table-scroll::-webkit-scrollbar-thumb:hover, .tab-sidebar::-webkit-scrollbar-thumb:hover { background: var(--text); }

/* Scoring log defender picker */
.stat-cell.expanded {
  grid-column: 1 / -1;
  text-align: left;
  background: var(--accent-light);
  border-color: var(--accent);
}
.defender-pick-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 6px 0;
}
.defender-pick-list button {
  padding: 6px 10px;
  font-size: 0.85rem;
  background: var(--panel-bg);
  color: var(--text);
  border: 1px solid var(--border);
}
.defender-pick-list button:hover {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
}

/* Without this, a picked defender/assist looks identical to an unpicked one — clicking a
   name appears to do nothing, which is exactly what makes Confirm feel unreliable. */
.defender-pick-list button.selected {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
  font-weight: 700;
  box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.5);
}
.defender-pick-list button.selected::before {
  content: "✓ ";
}

/* Same "picking one clearly did something" problem, generalized to any secondary-btn used as a
   toggle/tab (team direction, Best & Worst Individual Games' mode switch) -- without this, the
   `selected` class already added by several of these got no actual visual treatment at all. */
.secondary-btn.selected {
  background: var(--accent);
  color: white;
  border-color: var(--accent);
  font-weight: 700;
}

.confirm-row {
  display: flex;
  gap: 10px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.confirm-btn {
  flex: 1;
  padding: 12px 16px;
  font-size: 1rem;
}

/* Shot chart — click-to-place shot location on a field goal. No paint or free-throw line in
   Poolean's rules, just a straight 3pt line at 60% of the court depth from the hoop, so the
   diagram is deliberately this simple. */
.shot-chart-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin: 6px 0;
}
/* The pool is ~30ft end to end (the y axis, hoop to far wall) by ~15ft wide (the x axis) — a
   true 2:1 ratio, not the arbitrary portrait shape used before. The SVG itself stays a plain
   0-100 square viewBox with preserveAspectRatio="none", so this is purely a CSS box shape —
   stored coordinates are already percentages of the rendered box, not the viewBox, so nothing
   about the click math or stored data needs to change to match. */
.shot-chart {
  width: 140px;
  height: 280px;
  cursor: crosshair;
}
.shot-chart-court {
  fill: var(--surface-tint);
  stroke: var(--border);
  stroke-width: 1;
}
.shot-chart-3pt-line {
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 3 2;
}
.shot-chart-label {
  font-size: 6px;
  fill: var(--muted);
}
.shot-chart-hoop {
  fill: var(--muted);
}
.shot-chart-dot {
  fill: var(--accent);
  stroke: white;
  stroke-width: 1;
}

/* Player Shot Chart — every individual marked shot plotted at its real spot (as opposed to the
   heatmap's zone buckets). Semi-transparent fill so overlapping shots read as a visibly denser
   cluster rather than just stacking invisibly on top of each other. */
.shot-dot-make {
  fill: var(--success);
  fill-opacity: 0.72;
  stroke: var(--panel-bg);
  stroke-width: 0.4;
}
.shot-dot-miss {
  fill: var(--danger);
  fill-opacity: 0.72;
  stroke: var(--panel-bg);
  stroke-width: 0.4;
}
.shot-chart-legend {
  display: flex;
  gap: 14px;
  justify-content: center;
  margin-top: 8px;
  font-size: 0.82rem;
  color: var(--muted);
}
.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.legend-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
}
.legend-dot-make { background: var(--success); }
.legend-dot-miss { background: var(--danger); }

/* Heatmap (Player Detail + Leaderboard) — reuses the shot-chart court/3pt-line, flipped so the
   hoop renders at the bottom, with a grid of colored cells laid over it. Cell fill color is
   generated inline per-cell (a hue from red to green based on that zone's FG%), since it's
   data-driven rather than a fixed style. Width:height here must stay a 1:2 ratio, matching the
   SVG's own viewBox (see HEATMAP_VIEWBOX_W/H in app.js) — that's what keeps cells/text/the hoop
   circle rendering at their real proportions instead of stretched. */
.heatmap-chart {
  width: 200px;
  height: 400px;
  cursor: default;
}
.heatmap-cell-label {
  font-size: 7px;
  font-weight: 700;
  fill: white;
  stroke: rgba(0, 0, 0, 0.55);
  stroke-width: 0.5px;
  paint-order: stroke;
  pointer-events: none;
}

.heatmap-cell-pct {
  font-size: 4.5px;
  font-weight: 600;
  fill: white;
  stroke: rgba(0, 0, 0, 0.55);
  stroke-width: 0.4px;
  paint-order: stroke;
  pointer-events: none;
}

/* Backfill Shot Locations (Export tab) — a compact grid of mini shot charts, one click each,
   so going back through already-logged games doesn't mean reopening every one. */
.backfill-game-group {
  margin-bottom: 18px;
}
.backfill-game-group h4 {
  margin: 14px 0 8px;
}
.backfill-shot-rows {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.backfill-shot-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px;
  width: 104px;
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
.backfill-shot-label {
  font-size: 0.72rem;
  line-height: 1.3;
  text-align: center;
  color: var(--muted);
}
.backfill-shot-row .shot-chart {
  width: 70px;
  height: 140px;
}
.backfill-shot-row .secondary-btn {
  padding: 3px 8px;
  font-size: 0.72rem;
}
.backfill-shot-row-marked {
  border-color: var(--accent);
  opacity: 0.75;
}
.backfill-video-wrap {
  margin-bottom: 10px;
}
.backfill-video {
  width: 480px;
  max-width: 100%;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: black;
}
#backfillUndoToast {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 1.6em;
  margin: 4px 0 10px;
}
#backfillUndoToast:empty {
  margin: 0;
}

/* Highlight / lowlight reel */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
}
.badge-highlight {
  background: var(--success-light);
  color: var(--success);
}
.badge-lowlight {
  background: var(--danger-light);
  color: var(--danger);
}
.badge-video {
  background: var(--accent-light);
  color: var(--accent);
}
/* Boxed and colored like a real callout rather than plain .hint text, since it's easy to miss
   sitting right under the scoreboard otherwise — and it's a real setting (Shooting by Direction
   is silently blank without it, not just a nice-to-have label). */
.team-direction-toggle {
  margin-top: 10px;
  padding: 8px 12px;
  background: var(--accent-light);
  border: 1px solid var(--accent);
  border-radius: 6px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* A real status readout, not just relying on which button looks "selected" — the click itself
   should be unmistakable, not something you have to infer from a subtle background change. */
.team-direction-status {
  font-weight: 400;
  color: var(--muted);
}
.team-direction-status-set {
  font-weight: 700;
  color: var(--success);
}
.badge-review {
  background: var(--warning-light);
  color: var(--warning);
}
.badge-imbalanced {
  background: var(--warning-light);
  color: var(--warning);
}
.badge-past-season {
  background: var(--surface-muted);
  color: var(--muted);
}
.reel-time-input {
  width: 64px;
  padding: 5px 6px;
  font-size: 0.85rem;
}
.reel-note-input {
  width: 140px;
  padding: 5px 8px;
  font-size: 0.85rem;
}

/* Awards vs. Stats — each voted award alongside its winner(s) and their standing on the
   closest tracked stat. A simple card grid rather than a table, since the "detail" text per
   winner varies in length award to award (a rank, a trend arrow, an average delta) in a way
   that doesn't line up cleanly into fixed columns. */
.awards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 10px;
}
/* Awards vs. Stats sits in a .panel-row next to Power Ranking vs. Performance, and the two are
   naturally very different lengths (an expandable award list vs. a per-night table). The first
   attempt at fixing this only used align-items:stretch — making this panel a flex column with the
   grid as the flexible/scrolling child (flex:1 1 auto; min-height:0; overflow-y:auto) so it'd fill
   exactly the row's stretched height — but that only actually engages the scroll when this panel
   is the *shorter* one, since stretch expands every item in the row to match whichever is
   *tallest* natively. With a real, larger award list (11 awards, some multi-winner), this panel
   turned out to be the taller one most of the time, so the row just stretched Power Ranking up to
   match it instead — the opposite of "collapsed into scrollable." A flat `max-height` on the grid
   itself (not tied to the neighbor's height at all) guarantees scrolling kicks in once there's
   more than a screenful of awards, regardless of which panel happens to be taller this time. */
.panel:has(> #awardsVsStats) {
  display: flex;
  flex-direction: column;
}
.panel:has(> #awardsVsStats) #awardsVsStats {
  flex: 1 1 auto;
  min-height: 0;
  max-height: 500px;
  overflow-y: auto;
}
.award-row {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  background: var(--surface-tint);
}
.award-label {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--accent);
}
.award-toggle {
  background: none;
  border: none;
  padding: 0;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  cursor: pointer;
  margin-bottom: 8px;
  font-weight: normal;
}
.award-toggle-icon {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
}
.award-toggle:hover .award-toggle-icon,
.award-toggle:hover .award-label {
  color: var(--text);
}
.award-winners {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.award-winner {
  display: flex;
  flex-direction: column;
}
.award-winner-name {
  font-weight: 700;
}
.award-standings-wrap:not(:empty) {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
/* A grid item's default min-width is auto, not 0 — meaning it refuses to shrink below its own
   content's intrinsic width no matter how narrow the grid column actually is. Combined with
   .award-standings-name's white-space:nowrap below (a name plus a date, e.g. "Michael T — Wed,
   Jul 29"), that intrinsic width was often wider than half the panel on a phone, so this column
   pushed past its grid track and leaked out of the panel instead of ever actually wrapping. */
.award-standings-col {
  min-width: 0;
}
.award-standings-heading {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--muted);
}
.award-standings {
  margin: 0;
  padding-left: 18px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.award-standings li {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.82rem;
}
.award-standings-name {
  font-weight: 600;
}

/* Play Style Clusters (renderPlayStyleClusters() in app.js) — one card per k-means cluster, laid
   out however many columns actually fit rather than a fixed count, since k itself varies with
   roster size (2-4 clusters depending on how many qualifying players there are). */
.play-style-clusters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 14px;
  margin-top: 10px;
}
.play-style-cluster {
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  min-width: 0;
}
.play-style-cluster h4 {
  margin: 0 0 4px;
  font-family: var(--font-display);
  font-size: 0.92rem;
  display: flex;
  align-items: baseline;
  gap: 6px;
  flex-wrap: wrap;
}
.play-style-explain {
  margin: 0 0 8px;
  font-style: italic;
}
.play-style-cluster ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.play-style-cluster li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  min-width: 0;
}

/* Personalized Tips (renderPlayerTips() in app.js) */
.player-tips-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.player-tips-list li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  line-height: 1.45;
}
.player-tip-icon {
  flex-shrink: 0;
  font-size: 1.1rem;
  line-height: 1.45;
}
/* "Watch film" row (renderPlayerTips()/renderNotableMatchups() in app.js) — the actual
   improvement mechanism a bare percentage can't be: real dates, each one an openGame() jump
   straight into that game's own Shot Log, where the existing per-row Jump-to-video buttons take
   over from there. */
.player-tip-watch {
  margin-top: 6px;
  font-size: 0.8rem;
  color: var(--muted);
}
.player-tip-game-btn {
  display: inline-block;
  padding: 2px 8px;
  margin: 2px 2px 0 0;
  background: var(--surface-muted);
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--accent);
}
.player-tip-game-btn:hover {
  background: var(--surface-muted-hover);
}

/* Notable Matchups (renderNotableMatchups() in app.js) — same card-per-row shape as the
   Personalized Tips list above, reused directly since it's the same underlying signal just
   pooled league-wide instead of scoped to one player's own page. */
.notable-matchups-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.notable-matchups-list li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: var(--surface-tint);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  line-height: 1.45;
  font-size: 0.88rem;
}

.power-ranking-night {
  margin-bottom: 18px;
}
.power-ranking-night:last-child {
  margin-bottom: 0;
}
.power-ranking-night h4 {
  margin: 0 0 6px;
}

/* Two-Way Quadrant — Off Rating/20 vs. Def Rating/20 scatter, axes crossing at zero on both stats
   rather than at the data's own midpoint (see the comment above computeQuadrantData() in
   app.js for why zero is the meaningful line here). */
.quadrant-svg {
  width: 100%;
  max-width: 420px;
  display: block;
  margin: 4px auto 0;
}
.quadrant-axis {
  stroke: var(--muted);
  stroke-width: 1.4;
}
.quadrant-dot {
  fill: var(--accent);
  stroke: var(--panel-bg);
  stroke-width: 1.5;
}
/* The border ring drawn over a photo dot (svgAvatarDot() in app.js) — deliberately its own class
   rather than reusing .quadrant-dot: CSS `fill` overrides an SVG presentation attribute, so a
   `fill="none"` circle still classed `.quadrant-dot` would paint solid var(--accent) and hide the
   photo underneath it entirely. */
.quadrant-dot-ring {
  fill: none;
  stroke: var(--panel-bg);
  stroke-width: 1.5;
}
.quadrant-dot-initial {
  fill: white;
  font-size: 9px;
  font-weight: 700;
  font-family: var(--font-body);
  pointer-events: none;
}
.quadrant-label {
  font-size: 10px;
  font-weight: 600;
  fill: var(--text);
  font-family: var(--font-body);
}
.rank-line-path {
  fill: none;
  stroke-width: 2.5;
  opacity: 0.95;
}
.rank-line-label {
  font-size: 10px;
  font-weight: 700;
  font-family: var(--font-body);
}
.quadrant-axis-label {
  font-size: 10px;
  fill: var(--text);
  font-family: var(--font-mono);
}
.quadrant-corner-label {
  font-size: 8px;
  fill: var(--muted);
  font-family: var(--font-body);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* Shot Selection — one horizontal stacked bar per player, share of their own attempts by zone.
   Colors intentionally track typical shot quality (green=best, red=worst), same association as
   the League Shot Heatmap's FG% coloring. */
.shot-selection-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-bottom: 12px;
  font-size: 0.82rem;
  color: var(--muted);
}
.legend-swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  display: inline-block;
}
.shot-selection-bar {
  flex: 1;
  display: flex;
  height: 16px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-muted);
}
.shot-seg {
  height: 100%;
}
.shot-seg-close { background: var(--success); }
.shot-seg-mid { background: var(--warning); }
.shot-seg-line { background: var(--accent); }
.shot-seg-deep { background: var(--danger); }
.legend-swatch.shot-seg-close { background: var(--success); }
.legend-swatch.shot-seg-mid { background: var(--warning); }
.legend-swatch.shot-seg-line { background: var(--accent); }
.legend-swatch.shot-seg-deep { background: var(--danger); }

/* League TS% Over Time — one pooled line, not per-player, so this stays a simple line chart
   rather than reusing any of the multi-series patterns elsewhere in the app. */
.ts-line-svg {
  width: 100%;
  max-width: 560px;
  display: block;
  margin: 4px auto 0;
}
.ts-line-axis {
  stroke: var(--muted);
  stroke-width: 1.4;
}
.ts-line-path {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
}
.ts-line-dot {
  fill: var(--accent);
  stroke: var(--panel-bg);
  stroke-width: 1;
}
.ts-line-axis-label {
  font-size: 10px;
  font-weight: 600;
  fill: var(--text);
  font-family: var(--font-mono);
}
/* Season-average reference line on the Two-Way Trend chart — dashed so it reads as a backdrop to
   compare the real line against, not another data series, but still text-strength so it doesn't
   disappear the way the old --border-colored axis lines did. */
.ts-line-ref {
  stroke: var(--text);
  stroke-width: 1.2;
  stroke-dasharray: 4 3;
  opacity: 0.55;
}
/* The optional second reference line (renderTrendLineChart's own `leagueAvg` param) — a dotted
   accent-colored line rather than the season-average line's dashed neutral one, so the two stay
   visually distinct even when they land close together on the same chart. */
.ts-line-ref-league {
  stroke: var(--accent);
  stroke-width: 1.2;
  stroke-dasharray: 1.5 3;
  opacity: 0.7;
}
.ts-line-league-label {
  fill: var(--accent);
}

/* Head-to-Head Matchup Grid — reuses .matchup-table/.sticky-col, cell fill color is generated
   inline per-cell (same red-to-green FG% hue as the heatmap), since it's data-driven. */
.matchup-grid-cell {
  text-align: center;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  min-width: 56px;
}
.matchup-grid-empty {
  color: var(--muted);
  background: var(--surface-muted);
}

/* TS% by Shot Distance — a plain 4-bar chart, bar fill following the same red-to-green FG% hue
   as everywhere else color encodes shooting quality in this app. */
.ts-zone-svg {
  width: 100%;
  max-width: 420px;
  display: block;
  margin: 4px auto 0;
}
.ts-zone-value-label {
  font-size: 11px;
  font-weight: 700;
  fill: var(--text);
  font-family: var(--font-mono);
}
.ts-zone-axis-label {
  font-size: 10px;
  font-weight: 600;
  fill: var(--text);
  font-family: var(--font-body);
}
