/*
 * Brammell Basketball - 3-on-3 basketball stat tracker
 */

/* Barlow (SIL Open Font License, see fonts/OFL.txt), self-hosted, Latin
   subset only. Body text is Barlow; headings and scores are Barlow Semi
   Condensed. */
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/barlow-400-4ad4debe.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/barlow-500-1f5bcbda.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/barlow-600-07ba6ae4.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/barlow-700-a47e5b9b.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 800; font-display: swap; src: url("/assets/barlow-800-df7bd984.woff2") format("woff2"); }
@font-face { font-family: "Barlow Semi Condensed"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/barlow-semi-condensed-600-76832064.woff2") format("woff2"); }
@font-face { font-family: "Barlow Semi Condensed"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/barlow-semi-condensed-700-6e3ac2a0.woff2") format("woff2"); }

/* Every color the site uses, named once. Court blue (--accent) means "you can
   tap this"; green and red mean only a win or loss (or a point for or
   against), so the two never blur together. The highlight reel's dark panel
   keeps its own dark grays further down, and the in-game scoring buttons
   keep their own colors. */
:root {
  --font-body: "Barlow", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Barlow Semi Condensed", "Barlow", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --accent: #1f5b8f;
  --accent-dark: #17476f;
  --accent-soft: #e3ecf5;
  --accent-wash: #eef3f8;
  --accent-line: #c4d7ea;
  /* --accent, see-through, laid over a sorted column's cells. */
  --sorted-tint: rgb(31 91 143 / 7%);
  /* On the highlight reel's dark panel and over video. */
  --accent-bright: #5fb0f0;
  --accent-on-bright: #0b1a26;

  --win: #1d6f42;
  --win-soft: #e3f5e9;
  --win-line: #b7e2c6;
  --loss: #b23b3b;
  --danger: #a3302c;
  --danger-dark: #7a2420;
  --danger-soft: #fdeaea;
  --danger-line: #f3bcbc;
  --warn: #8a6100;
  --warn-soft: #fff4d6;
  --warn-line: #ffe08a;

  /* Concrete neutrals, warm rather than blue-gray. */
  --ink: #2b2d30;
  --muted: #66655f;
  --faint: #8a8983;
  --surface: #ffffff;
  --ground: #f5f5f3;
  --ground-alt: #f9f9f7;
  --fill-soft: #f1f0ec;
  --fill: #ecebe7;
  --fill-strong: #e3e2dd;
  --band: #f0efeb;
  --band-strong: #e6e5e0;
  --line-soft: #efeeea;
  --line: #e4e3df;
  --line-strong: #cfcec8;
  --line-heavy: #aeada6;

  --radius: 4px;
  --radius-lg: 6px;

  /* The phone's status bar and home bar, which a Home Screen copy draws
     under (see the layout's status-bar-style); 0 in the browser. */
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}

* {
  box-sizing: border-box;
}

/* Form controls don't inherit the page font by default. */
button, input, select, textarea {
  font-family: inherit;
}

body {
  font-family: var(--font-body);
  background: var(--ground);
  color: var(--ink);
  margin: 0;
  /* Clear of the status bar, and sideways of the notch. */
  padding: var(--safe-top) env(safe-area-inset-right, 0px) 0 env(safe-area-inset-left, 0px);
}

/* Behind the status bar's white clock. */
body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--safe-top);
  background: #000;
}

.container {
  padding: 24px 16px 64px;
}

/* The full logo above the log-in and account setup forms (see
   shared/_brand_lockup.html.erb). */
.brand-lockup {
  margin: 8px 0 24px;
}

.brand-lockup img {
  display: block;
  max-width: 100%;
  height: auto;
}

/* --- Site nav (layouts/application.html.erb) --- */
.site-nav {
  position: relative;
  z-index: 20;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}

.site-nav-inner {
  padding: 8px 16px;
  min-height: 52px;
  display: flex;
  align-items: center;
  gap: 14px;
}

.site-brand {
  display: block;
  flex: none;
  padding: 6px 0;
}

.site-brand img {
  display: block;
  height: 20px;
  width: auto;
}

.site-nav-links {
  display: flex;
  align-items: center;
  gap: 2px;
}

.site-nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 10px;
  border-radius: var(--radius);
  color: var(--muted);
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: none;
  cursor: pointer;
}

.site-nav-link:hover,
.nav-menu[open] > .site-nav-link {
  background: var(--ground);
  color: var(--ink);
}

.site-nav-link[aria-current="page"] {
  color: var(--ink);
}

.site-nav-auth {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Dropdowns (nav_menu_controller.js): a <details> whose <summary> is the
   button. */
.nav-menu {
  position: relative;
}

.nav-menu > summary {
  list-style: none;
}

.nav-menu > summary::-webkit-details-marker {
  display: none;
}

.nav-menu > summary.site-nav-link::after {
  content: "";
  width: 6px;
  height: 6px;
  margin: -3px 0 0 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}

.nav-menu-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 30;
  display: grid;
  min-width: 220px;
  max-width: min(320px, calc(100vw - 32px));
  max-height: 70vh;
  overflow-y: auto;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 10px 28px rgba(43, 45, 48, 0.14);
}

.account-menu .nav-menu-panel {
  left: auto;
  right: 0;
}

.nav-menu-panel a,
.nav-menu-panel button {
  display: block;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--ink);
  font-size: 0.95rem;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.nav-menu-panel a:hover,
.nav-menu-panel button:hover {
  background: var(--ground);
}

.nav-menu-panel form {
  margin: 0;
}

.nav-menu-heading {
  padding: 6px 10px 10px;
  color: var(--muted);
  font-size: 0.8rem;
  overflow-wrap: anywhere;
}

.nav-menu-heading strong {
  color: var(--ink);
  font-weight: 600;
}

.nav-menu-divider {
  margin: 4px 0;
  border: 0;
  border-top: 1px solid var(--line);
}

.avatar {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--ink);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  cursor: pointer;
}

.account-menu[open] > .avatar,
.avatar:hover {
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* On every page while you have a game in progress (except that game's). */
.live-pill {
  display: inline-flex;
  align-items: center;
  max-width: 240px;
  min-height: 30px;
  padding: 0 12px 0 10px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--accent-dark);
  font-size: 0.82rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}

.live-pill:hover {
  background: var(--accent-line);
}

.live-label {
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 600;
}

.live-dot {
  flex: none;
  width: 8px;
  height: 8px;
  margin-right: 7px;
  border-radius: 50%;
  background: var(--accent);
  animation: live-pulse 1.8s ease-out infinite;
}

@keyframes live-pulse {
  0% { box-shadow: 0 0 0 0 rgba(31, 91, 143, 0.45); }
  100% { box-shadow: 0 0 0 7px rgba(31, 91, 143, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .live-dot {
    animation: none;
  }
}

/* --- Page bar: breadcrumbs left, page actions right --- */
.sub-nav {
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}

.sub-nav-inner {
  padding: 8px 16px;
  min-height: 46px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 20px;
  font-size: 0.85rem;
}

.breadcrumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--faint);
}

.breadcrumbs li {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.breadcrumbs li + li::before {
  content: "›";
  color: var(--faint);
}

.breadcrumbs a {
  color: var(--accent);
  font-weight: 600;
  text-decoration: none;
}

.breadcrumbs a:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.breadcrumbs [aria-current="page"] span {
  max-width: 36ch;
  overflow: hidden;
  color: var(--ink);
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.page-actions {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.page-actions .btn-link {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
}

.page-actions .btn-link:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.page-actions .btn-link.is-primary {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}

.page-actions .btn-link.is-primary:hover {
  border-color: var(--accent-dark);
  background: var(--accent-dark);
}

.page-actions .btn-link[hidden] {
  display: none;
}

/* Phones: the wordmark is the way home, the Live link drops its game name,
   and the trail shortens to a back link to the page above this one. */
@media (max-width: 600px) {
  .site-nav-inner {
    gap: 8px;
  }

  .site-nav-home-link,
  .live-label {
    display: none;
  }

  .breadcrumbs li {
    display: none;
  }

  .breadcrumbs li.is-parent {
    display: flex;
  }

  .breadcrumbs li.is-parent::before {
    content: none;
  }

  .breadcrumbs li.is-parent a::before {
    content: "‹ ";
  }
}

.btn-link {
  font-size: inherit;
}

.btn-link {
  display: inline-block;
  padding: 0;
  border: none;
  background: none;
  color: var(--accent);
  font-size: 0.85rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.btn-link:hover {
  color: var(--accent-dark);
}

/* .btn-link's own display:inline-block otherwise wins the cascade over the
   UA stylesheet's [hidden] { display: none }, since both have equal
   specificity and this app's stylesheet loads later — silently defeating
   the hidden attribute on any button using this class. */
.btn-link[hidden] {
  display: none;
}

h1, h2 {
  font-family: var(--font-display);
  font-weight: 700;
}

h1 {
  font-size: 1.75rem;
  margin-bottom: 0.25rem;
}

h2 {
  font-size: 1.3rem;
  margin: 1.5rem 0 0.75rem;
}

a {
  color: var(--accent);
}

.flash {
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
  font-size: 0.95rem;
}

.flash-notice {
  background: var(--win-soft);
  color: var(--win);
  border: 1px solid var(--win-line);
}

.flash-alert {
  background: var(--danger-soft);
  color: var(--danger);
  border: 1px solid var(--danger-line);
}

/* Saving the site to the Home Screen, offered on phones on the first
   visit (see home_screen_hint_controller.js). */
.home-screen-hint {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 16px;
  padding: 8px 4px 8px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.92rem;
  line-height: 1.4;
}

.home-screen-hint[hidden] {
  display: none;
}

.home-screen-hint p {
  flex: 1;
  margin: 0;
}

.home-screen-hint svg {
  vertical-align: -2px;
}

.home-screen-hint-install {
  flex: none;
}

.home-screen-hint-close {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: none;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.btn {
  display: inline-block;
  padding: 8px 14px;
  border-radius: var(--radius);
  border: 1px solid transparent;
  background: var(--accent);
  color: #fff;
  font-size: 0.95rem;
  cursor: pointer;
  text-decoration: none;
}

.btn:disabled,
.btn.disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.btn[hidden] {
  display: none;
}

.btn-point {
  background: var(--win);
}

.btn-against {
  background: var(--loss);
}

.btn-shot {
  background: #2563eb;
  border-color: #2563eb;
}

.btn-turnover {
  background: #d9730d;
  border-color: #d9730d;
}

/* Toggled on the roster's turbo-frame by possession_controller.js — filled
   with the shot/turnover color while the tracked team is on offense, a
   neutral gray fill with a thicker colored outline while on defense, so
   the two states read as clearly distinct at a glance. */
.possession-defense .btn-shot,
.possession-defense .btn-turnover {
  background: var(--fill);
  border-width: 2px;
}

.possession-defense .btn-shot {
  color: #2563eb;
}

.possession-defense .btn-turnover {
  color: #d9730d;
}

.btn-turnover-off {
  background: #a15c1f;
}

.btn-turnover-def {
  background: #c47f1f;
}

/* Matches .btn-secondary's subtle treatment elsewhere — a substitution is a
   secondary action next to Shot/Turnover, not one that should compete with
   them for attention, and doesn't need its own offense/defense styling the
   way those do. */
.btn-bench,
.btn-bench.active-bench {
  background: var(--surface);
  color: var(--ink);
  border: 2px solid var(--line-heavy);
}

.btn-bench:hover,
.btn-bench.active-bench:hover {
  background: var(--ground-alt);
}

.btn-finish {
  background: var(--danger);
}

.btn-timeout {
  background: #b8860b;
}

.btn-start {
  background: var(--accent);
}

/* Voice entry (see voice_entry_controller.js): red while the mic is on. */
.btn-voice {
  background: var(--surface);
  color: var(--accent);
  border-color: var(--accent);
}

.is-listening .btn-voice {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}

.voice-status {
  margin: 10px 0 0;
  font-size: 0.85rem;
  color: var(--muted);
}

.btn-secondary {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-strong);
}

.btn-danger {
  background: var(--loss);
}

.game-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}

.undo-row {
  margin-top: 10px;
}

.btn-undo {
  display: inline-block;
  padding: 4px 8px;
  border: none;
  background: transparent;
  color: var(--faint);
  font-size: 0.78rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.btn-undo:hover {
  color: var(--muted);
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  margin-bottom: 16px;
}

form.card {
  max-width: 480px;
}

.team-list, .game-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.team-list li, .game-list li {
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}

.team-list li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}

.team-list li .name {
  flex: 1;
}

/* "You're a fan of this team" on a followed team's page, with a way to
   stop. */
.fan-note {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 12px;
}

.fan-note form {
  display: inline;
}

.scoreboard {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 8px;
}

.scoreboard .score {
  font-family: var(--font-display);
  font-size: 2.5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.scoreboard .score.for {
  color: var(--win);
}

/* Score adjustments counted in the scoreboard just above (see
   games/_score_adjustments.html.erb) — quiet, since they're rare. */
.score-adjustments {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  color: var(--faint);
  font-size: 0.78rem;
}

.score-adjustment-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--ground);
  color: var(--muted);
  border-radius: 999px;
  padding: 2px 10px;
  font-variant-numeric: tabular-nums;
}

.score-adjustment-chip form {
  display: inline;
}

.score-adjustment-remove {
  border: none;
  background: transparent;
  color: var(--faint);
  font-size: 0.9rem;
  line-height: 1;
  padding: 0 0 0 2px;
  cursor: pointer;
}

.score-adjustment-remove:hover {
  color: var(--danger);
}

.scoreboard.small {
  gap: 32px;
  justify-content: flex-start;
}

.scoreboard.small .score {
  font-size: 1.4rem;
}

.stats-section {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 12px;
  margin-bottom: 14px;
}

.stats-section summary {
  font-weight: 600;
  font-size: 1rem;
  cursor: pointer;
}

.stats-section[open] summary {
  margin-bottom: 10px;
}

.stats-section summary a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.stats-section summary a:hover {
  color: var(--accent);
}

.stats-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 10px;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
}

.stats-table th, .stats-table td {
  text-align: left;
  padding: 7px 5px;
  border-bottom: 1px solid var(--line-soft);
}

.stats-table th {
  color: var(--muted);
  font-weight: 600;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* A stats table's header rows read as one tinted block, in the accent
   since clicking a header sorts, so they're easy to pick out from the
   numbers without shouting over them. */
.box-score-table thead th {
  background: var(--fill-soft);
  color: var(--accent-dark);
}

.stat-group-boundary {
  border-left: 2px solid var(--line-strong);
}

.stats-table td:not(:first-child), .stats-table th:not(:first-child) {
  text-align: right;
}

/* The shooting columns (FG/FGA/FG%/TMRB/TMRB%) three ways (FG, 1-point,
   2-point) and the free throws (FT/FTA/FT%) three ways (all, 1-point,
   2-point), each under a shared label (teams/_stat_group_labels), so the
   same stats can be compared without repeating "1PT"/"2PT" in every
   column header. Quieter than the stat headers under them — lighter,
   smaller, with a thin rule marking which columns they cover — so the
   eye lands on the stat names first. */
.stats-table th.stat-group-label {
  padding: 4px 5px 2px;
  border-bottom-color: var(--line-strong);
  color: var(--accent);
  font-weight: 500;
  font-size: 0.68rem;
  text-align: center;
}

/* Centered over its columns, but slides along to stay readable just right
   of the pinned player column while any of its columns are still showing
   (sortable_table_controller.js measures that column). */
.stat-group-name {
  position: sticky;
  left: calc(var(--stats-name-width, 0px) + 8px);
  right: 8px;
  display: inline-block;
}

table[data-controller~="sortable-table"] th {
  cursor: pointer;
  user-select: none;
}

table[data-controller~="sortable-table"] th:hover {
  color: var(--ink);
}

table[data-controller~="sortable-table"] th.sorted-asc::after,
table[data-controller~="sortable-table"] th.sorted-desc::after {
  margin-left: 4px;
  color: var(--ink);
}

table[data-controller~="sortable-table"] th.sorted-asc::after {
  content: "\25B2";
}

table[data-controller~="sortable-table"] th.sorted-desc::after {
  content: "\25BC";
}

/* The sorted column, tinted from header to footer (see markSortedColumn).
   A layer over each cell's own background, so the row stripes, hover and
   the Team row still show through it. */
.stats-table .is-sorted {
  background-image: linear-gradient(var(--sorted-tint), var(--sorted-tint));
}

.stats-table th.is-sorted {
  color: var(--ink);
}

.tournament-group {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 14px;
}

.tournament-group > summary {
  font-weight: 700;
  font-size: 1.05rem;
  cursor: pointer;
}

.tournament-group[open] > summary {
  margin-bottom: 14px;
}

.tournament-group > summary a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.tournament-group > summary a:hover {
  color: var(--accent);
}

.tournament-group > .stats-section {
  background: var(--ground-alt);
}

.game-group {
  margin-left: 20px;
  padding-left: 16px;
  border-left: 2px solid var(--line);
}

.scoreboard .score.against {
  color: var(--loss);
}

.score-box {
  border-radius: var(--radius);
  padding: 4px 8px;
}

.score-box-clickable {
  cursor: pointer;
}

.score-box-clickable:hover {
  background: var(--ground-alt);
}

/* Subtle by design (see possession_controller.js) — a small dot next to
   whichever team's name currently has the ball, not a banner or border
   that would compete with the score itself for attention. */
.possession-dot {
  color: #d38b00;
  font-size: 0.6rem;
  vertical-align: middle;
}

.possession-hint {
  margin: 4px 0 0;
  font-size: 0.75rem;
  color: var(--faint);
}

/* flex-wrap: nowrap deliberately — this row has to survive down to a
   fairly narrow stats pane (see .video-workspace) without its buttons
   wrapping onto a second line, since a button that's sometimes on its own
   row and sometimes not is much harder to reliably tap. .player-name is
   the one column allowed to give up space (down to a 40px floor,
   truncating with an ellipsis below that) — the three button columns are
   flex-shrink: 0, always at their full compact size, so all the squeeze
   lands on the name instead of the controls. */
.player-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 8px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line);
}

.player-row:last-child {
  border-bottom: none;
}

.roster-header-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 8px;
  padding-bottom: 6px;
}

/* Matches .player-name's own flex sizing below so the header stays aligned
   with its column regardless of how much room the name ends up with. */
.roster-header-row .name-column {
  flex: 1 1 auto;
  min-width: 0;
}

.player-row.benched .player-name {
  color: var(--faint);
}

.player-name {
  font-weight: 600;
  flex: 1 1 auto;
  /* Shrinks to make room for the (fixed-size) button columns before
     anything else does, down to this floor, then truncates rather than
     forcing the row wider or wrapping. */
  min-width: 40px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.player-stats {
  font-size: 0.85rem;
  color: var(--muted);
  min-width: 90px;
}

.player-actions {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
}

/* Slightly more compact than the base .btn — the tighter padding/font is
   what makes "Shot"/"Turnover" comfortably fit side by side in a 148px
   .player-action-group without wrapping; Call Timeout/Finish Game/etc.
   elsewhere are unaffected since this only targets buttons inside the
   roster's action columns. */
.player-actions .btn,
.player-sub-group .btn {
  padding: 6px 8px;
  font-size: 0.85rem;
}

.player-action-group {
  flex: 0 0 auto;
  width: 148px;
  border-right: 1px solid var(--line);
  padding-right: 8px;
}

.player-sub-group {
  flex: 0 0 auto;
  width: 52px;
  border-left: 1px solid var(--line);
  padding-left: 8px;
}

.player-action-label,
.player-sub-label {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--faint);
  white-space: nowrap;
}

.player-sub-label {
  border-left: 1px solid var(--line);
  padding-left: 8px;
}

.player-action-label {
  flex: 0 0 auto;
  width: 148px;
  border-right: 1px solid var(--line);
  padding-right: 8px;
  /* Stands out more than the plain "Sub" header next to it — this one
     doubles as the roster's current-possession readout, not just a static
     column label (see possession_controller.js). */
  font-size: 0.78rem;
  color: var(--ink);
}

.player-sub-label {
  flex: 0 0 auto;
  width: 52px;
}

/* The wizard card is narrower than the live scoring page's full-width
   roster, so the name column needs a lower floor before truncating here —
   the button columns are already compact enough at their normal size
   (see .player-action-group and friends above) to not need their own
   scaled-down override anymore. */
.new-event-players .player-name {
  min-width: 32px;
}

.roster-divider {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 6px 0;
  color: var(--faint);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.roster-divider::before,
.roster-divider::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--line);
}

.selection-banner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  color: var(--warn);
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 14px;
}

.selection-banner[hidden] {
  display: none;
}

.game-stats.selecting .player-row:not(.selectable) {
  opacity: 0.35;
  pointer-events: none;
}

.game-stats.selecting .player-row .btn-shot,
.game-stats.selecting .player-row .btn-turnover {
  opacity: 0.35;
  pointer-events: none;
}

.game-stats.selecting .btn-start,
.game-stats.selecting .btn-timeout,
.game-stats.selecting .btn-voice,
.game-stats.selecting .tracking-voice-button,
.game-stats.selecting .btn-finish,
.game-stats.selecting .undo-row {
  opacity: 0.35;
  pointer-events: none;
}

.player-row.selectable {
  outline: 2px solid #ffb400;
  outline-offset: -2px;
  border-radius: var(--radius);
}

.field {
  margin-bottom: 14px;
}

.field label {
  display: block;
  font-weight: 600;
  margin-bottom: 4px;
}

.field input[type="text"],
.field input[type="url"],
.field input[type="email"],
.field input[type="password"],
.field input[type="date"],
.field input[type="datetime-local"],
.field input[type="number"] {
  width: 100%;
  padding: 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: 1rem;
}

.field-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.field-row[hidden] {
  display: none;
}

.field-row .field {
  flex: 1;
}

.field-group {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 4px 14px 14px;
  margin-bottom: 14px;
}

.field-group legend {
  font-weight: 600;
  padding: 0 6px;
}

.error-list {
  background: var(--danger-soft);
  border: 1px solid var(--danger-line);
  color: var(--danger);
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
}

.top-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.top-actions h1 {
  margin: 0;
}

/* The one line under a page's name: what it belongs to and how it's going. */
.page-context {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 0.92rem;
}

.page-context .record-pill {
  display: inline-block;
  margin-right: 6px;
  background: var(--surface);
}

.page-context .meta-info {
  color: inherit;
  font-size: inherit;
}

.replay-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.replay-stat-summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 6px 10px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.85rem;
  color: var(--muted);
}

.replay-stat-summary:hover {
  background: var(--ground);
  color: var(--ink);
}

.replay-stat-time {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  min-width: 2.5em;
}

.replay-stat-focused {
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 12px 14px;
  background: var(--accent-wash);
}

.replay-field-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}

.replay-field-row[hidden] {
  display: none;
}

.replay-field-row label {
  min-width: 90px;
  font-weight: 600;
  font-size: 0.85rem;
}

.replay-field-row select {
  flex: 1;
  padding: 5px 6px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.replay-timestamp-input {
  width: 56px;
  padding: 5px 6px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.replay-actions-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 4px;
}

.replay-save-button {
  margin-top: 0;
}

.replay-errors {
  color: var(--danger);
  font-size: 0.85rem;
  margin-bottom: 8px;
}

.replay-delete-link {
  border: 0;
  background: none;
  padding: 0;
  color: var(--danger);
  font-size: 0.85rem;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.replay-delete-link:hover {
  color: var(--danger-dark);
}

.replay-delete-confirm {
  color: var(--danger);
  font-size: 0.85rem;
}

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

.filter-checkboxes {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 14px;
}

.filter-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  cursor: pointer;
}

.warning-banner {
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  color: var(--warn);
  padding: 10px 14px;
  border-radius: var(--radius);
  margin-bottom: 16px;
}

.stats-table-wrapper {
  overflow-x: auto;
}

.stats-table-title {
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin: 30px 0 0;
}

.stats-table-title:first-of-type {
  margin-top: 20px;
}

.stats-table-hint {
  font-size: 0.78rem;
  color: var(--faint);
  margin: 2px 0 0;
}

/* Scrolls sideways when its tabs don't fit (on a phone). The rule under the
   tabs is an inset shadow rather than a border so the active tab's
   underline can still cover it inside the scrolling box. */
.stat-tabs-nav {
  display: flex;
  gap: 4px;
  margin-top: 8px;
  box-shadow: inset 0 -1px 0 var(--line);
  overflow-x: auto;
  scrollbar-width: none;
}

.stat-tab {
  flex-shrink: 0;
  white-space: nowrap;
  background: none;
  border: none;
  padding: 8px 14px 7px;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--faint);
  border-bottom: 2px solid transparent;
  cursor: pointer;
}

.stat-tab:hover {
  color: var(--ink);
}

.stat-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

.stat-tab-panel[hidden] {
  display: none;
}

/* A views tab bar with the Team | Opp switch at its end (see
   teams/_sided_stat_views). The rule under the tabs moves out to span the
   whole row; when they don't both fit, the switch wraps to its own line
   above the tabs rather than below them. */
.stat-tabs-bar {
  display: flex;
  flex-wrap: wrap-reverse;
  align-items: center;
  gap: 6px 12px;
  margin-top: 8px;
  box-shadow: inset 0 -1px 0 var(--line);
}

.stat-tabs-bar > .stat-tabs-nav {
  margin-top: 0;
  min-width: 0;
  box-shadow: none;
}

.stat-tabs-bar > .side-switch {
  margin-left: auto;
}

/* The switch on its own, over views shown without tabs. */
.stat-side-row {
  display: flex;
  justify-content: flex-end;
  margin: 8px 0;
}

/* Same segmented look as the primary tab bars, a size smaller. */
.side-switch {
  display: inline-flex;
  gap: 2px;
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 2px;
}

.side-switch-option {
  background: none;
  border: none;
  border-radius: 3px;
  padding: 3px 12px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--faint);
  cursor: pointer;
}

.side-switch-option:hover {
  color: var(--ink);
}

.side-switch-option.is-active {
  background: var(--surface);
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(26, 26, 26, 0.12);
}

/* Segmented-track variant: for the outermost tab bar in a given region only
   (page-level tabs, or a stat-category bar with no enclosing tabs above it).
   Nested tab bars underneath it (e.g. Totals/Per Game/Per 20) keep the plain
   .stat-tab underline style above, unmodified. */
.stat-tabs-nav.primary {
  display: inline-flex;
  max-width: 100%;
  gap: 2px;
  margin-top: 4px;
  background: var(--fill);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 3px;
  box-shadow: none;
}

.stat-tab.primary {
  border: none;
  border-radius: 3px;
  margin-bottom: 0;
  padding: 6px 14px;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  color: var(--faint);
}

.stat-tab.primary:hover {
  color: var(--ink);
}

.stat-tab.primary.is-active {
  background: var(--surface);
  color: var(--ink);
  border-bottom-color: transparent;
  box-shadow: 0 1px 2px rgba(26, 26, 26, 0.12);
}

.stat-key-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 4px;
}

.stats-table-hint.list-hint {
  margin: 0 0 8px;
}

.pagination {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 12px;
}

.meta-info {
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--faint);
}

.meta-info a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.meta-info a:hover {
  color: var(--accent);
}

/* --- Shared pieces: section labels, card headers, results --- */

/* Echoes the BASKETBALL line under the logo: small, wide-spaced capitals. */
.section-label {
  margin: 0;
  color: var(--muted);
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}

.card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-bottom: 6px;
}

.card-head-link {
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
}

.card-head-link:hover {
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* W or L by the game score (GamesHelper#result_chip). An empty one holds a
   tie's place so rows still line up. */
.result-chip {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 3px;
  font-size: 0.72rem;
  font-weight: 800;
}

.result-chip.is-win {
  background: var(--win-soft);
  color: var(--win);
}

.result-chip.is-loss {
  background: var(--danger-soft);
  color: var(--loss);
}

.record-pill {
  flex: none;
  padding: 1px 9px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.play-icon {
  width: 0;
  height: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  border-left: 8px solid currentColor;
}

/* --- Team insights (teams/show.html.erb, TeamInsights) --- */
.insight-list {
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
}

.insight {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 12px;
  align-items: baseline;
  padding: 10px 0;
  border-top: 1px solid var(--line-soft);
}

.insight:first-child {
  border-top: none;
}

/* .insight's display:grid would otherwise beat [hidden] (see .btn-link[hidden]). */
.insight[hidden] {
  display: none;
}

.insight-more {
  margin-top: 4px;
}

.insight-tag {
  grid-column: 1 / -1;
  color: var(--faint);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.insight-text {
  margin: 2px 0 0;
  line-height: 1.4;
}

.insight-links {
  display: flex;
  gap: 12px;
}

.insight-link {
  font-size: 0.85rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.insight-link:hover {
  text-decoration: underline;
}

/* --- Home page (home/index.html.erb) --- */
.home-name {
  color: inherit;
  text-decoration: none;
}

.home-name:hover {
  color: var(--accent);
}

.resume-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  padding: 12px 14px;
  border: 1.5px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

.resume-banner:hover {
  background: var(--accent-wash);
}

.resume-banner .live-dot {
  margin-right: 0;
}

.resume-banner-text {
  flex: 1;
  min-width: 0;
}

.resume-banner::after {
  content: "›";
  color: var(--accent);
  font-size: 1.3rem;
  line-height: 1;
}

.last-game {
  display: grid;
  gap: 14px;
}

.last-game-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}

/* Full width like the stats pages, but the score itself stays together in
   the middle rather than spreading to the card's edges. */
.last-game-score {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  text-align: center;
}

.last-game-side {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.score-num {
  font-family: var(--font-display);
  font-size: 3rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.score-num.is-trailing {
  color: var(--faint);
}

.last-game-team {
  overflow-wrap: anywhere;
  font-size: 0.9rem;
  font-weight: 600;
}

/* Your own numbers in the game: labels like the section labels, numbers
   like the score. */
.stat-line {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin: 0;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}

.stat-line div,
.season-strip div {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
}

.stat-line dt,
.season-strip dt {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.stat-line dd,
.season-strip dd {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}

.last-game-actions {
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 8px;
}

.last-game-actions .btn {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  min-height: 42px;
  font-weight: 600;
}

/* Same dark as the highlight reel's panel, since that's where it goes. */
.btn-reel {
  background: #15181d;
}

.btn-reel:hover {
  background: #2a2f37;
}

.season-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin: 0 0 16px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.season-strip div {
  padding: 10px 8px;
}

.season-strip div + div {
  border-left: 1px solid var(--line);
}

.game-rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

.game-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
}

.game-row:last-child {
  border-bottom: 0;
}

.game-row-main {
  display: grid;
  flex: 1;
  min-width: 0;
}

.game-row-opponent {
  overflow: hidden;
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.game-row-opponent:hover {
  color: var(--accent);
}

.game-row-score {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.reel-link,
.reel-link-placeholder {
  flex: none;
  width: 32px;
  height: 32px;
}

.reel-link {
  display: grid;
  place-items: center;
  padding-left: 2px;
  border-radius: 50%;
  background: #15181d;
  color: #fff;
}

.reel-link:hover {
  background: var(--accent);
}

.home-columns {
  display: grid;
  gap: 0 16px;
}

@media (min-width: 760px) {
  .home-columns {
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    align-items: start;
  }
}

.link-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.link-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--line-soft);
}

.link-list li:last-child {
  border-bottom: 0;
}

.link-list a {
  min-width: 0;
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

.link-list a:hover {
  color: var(--accent);
}

.link-list-main {
  display: grid;
  min-width: 0;
}

/* Text for screen readers only, e.g. an icon button's name. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Phones: links and buttons get a bigger area to hit without looking any
   different, so they're easier to tap courtside. */
@media (max-width: 600px) {
  .btn {
    min-height: 40px;
  }

  .btn-link,
  .card-head-link,
  .link-list a,
  .game-row-opponent,
  .breadcrumbs a {
    padding-block: 8px;
    margin-block: -8px;
  }

  .reel-link,
  .reel-link-placeholder {
    width: 38px;
    height: 38px;
  }

  /* Already full-size buttons. */
  .page-actions .btn-link {
    margin-block: 0;
  }
}

.invitations-card {
  border-color: var(--warn-line);
  background: var(--warn-soft);
}

.first-steps {
  display: grid;
  justify-items: start;
  gap: 12px;
  max-width: 480px;
}

.first-steps .brand-lockup {
  margin: 0 0 4px;
}

.first-steps h2 {
  margin: 0;
}

.first-steps-list {
  margin: 0;
  padding-left: 1.3em;
  display: grid;
  gap: 4px;
}

.stats-table.box-score-table {
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
}

.stats-table tbody tr:nth-child(even) {
  background: var(--ground-alt);
}

.stats-table tbody tr:hover {
  background: var(--accent-wash);
}

.stats-table .stats-name {
  font-weight: 600;
  white-space: nowrap;
}

/* The player page's games, like the team page's Games list: the opponent,
   with the date, label and tournament under it. */
.stats-table .stats-game .meta-info {
  display: block;
}

.stats-table td:not(.stats-name),
.stats-table th:not(:first-child) {
  min-width: 30px;
}

/* The player column stays put while the rest scrolls sideways, with a line
   along its edge so it reads as pinned. In the group-label row only the
   corner cell above it is pinned (see teams/_stats_corner). */
.stats-table-wrapper table.stats-table th:first-child,
.stats-table-wrapper table.stats-table td.stats-name {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  box-shadow: inset -1px 0 0 var(--line);
}

.stats-table-wrapper table.box-score-table thead th:first-child {
  background: var(--fill-soft);
}

.stats-table tbody tr:nth-child(even) td.stats-name {
  background: var(--ground-alt);
}

.stats-table tbody tr:hover td.stats-name {
  background: var(--accent-wash);
}

.stats-total-row {
  font-weight: 700;
  border-top: 2px solid var(--line-strong);
}

.stats-total-row td.stats-name,
.stats-total-row.stats-opponent-row td.stats-name {
  background: var(--fill-soft);
}

.stats-opponent-row {
  color: var(--danger);
  border-top: none;
}

/* Rendered with dialog.show() (not showModal()) and positioned absolutely
   within .game-stats, rather than the browser's top layer, so it stays
   contained to the player list and never covers or blocks the video. */
.stat-wizard {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
  border: none;
  background: rgba(0, 0, 0, 0.45);
  z-index: 20;
}

.stat-wizard[open] {
  display: flex;
  align-items: flex-start;
  justify-content: center;
}

.stat-wizard[open] > form {
  width: 100%;
}

.wizard-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 20px;
  width: 100%;
  max-height: 100%;
  overflow-y: auto;
}

/* The substitutions modal lists every substitution in the game: the header
   stays put while just the list scrolls, opened to the next one coming up. */
[data-edit-substitutions-modal-target="dialog"] .wizard-card {
  display: flex;
  flex-direction: column;
}

.substitutions-list {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.substitution-add-row {
  margin-bottom: 10px;
}

.substitution-add {
  flex-shrink: 0;
}

.substitution-add[hidden] {
  display: none;
}

[data-edit-substitutions-modal-target="dialog"] .field select {
  width: 100%;
  padding: 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font-size: 1rem;
  background: var(--surface);
}

.substitution-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.substitution-actions[hidden] {
  display: none;
}

.substitution-delete p {
  margin: 0 0 10px;
}

.substitution-upcoming {
  background: var(--accent-wash);
  border-color: var(--accent-line);
  box-shadow: inset 3px 0 0 var(--accent);
}

.substitution-upcoming-tag {
  margin-left: 8px;
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.wizard-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14px;
}

/* The stat wizard's header additionally carries a Back button and a side
   label alongside the heading — scoped to its own dialog (rather than the
   shared .stat-wizard positioning class every other modal also uses) so
   this doesn't affect the plain heading + close button layout elsewhere. */
[data-stat-wizard-target="dialog"] .wizard-header {
  justify-content: flex-start;
  gap: 8px;
}

[data-stat-wizard-target="dialog"] .wizard-close {
  margin-left: auto;
}

.wizard-heading {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}

.wizard-back {
  border: 0;
  background: none;
  padding: 0;
  color: var(--muted);
  font-size: 0.85rem;
  cursor: pointer;
  white-space: nowrap;
}

.wizard-back:hover {
  color: var(--ink);
}

.wizard-back[hidden] {
  display: none;
}

.wizard-side-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.wizard-voice-heard {
  margin: -6px 0 12px;
  font-size: 0.85rem;
  font-style: italic;
  color: var(--muted);
}

/* The voice review card (see openFromVoice): every part of the stat on one
   card. A part still to answer is marked in the accent; an assist, which
   most made shots don't have, gets the same mark without holding up Save. */
.voice-review-summary {
  color: var(--muted);
}

.voice-row {
  margin: 0 0 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.voice-row.needs-answer {
  border-color: var(--accent);
  background: var(--accent-wash);
}

.wizard-step .voice-row-label {
  font-size: 0.85rem;
  margin-bottom: 6px;
}

.voice-row-hint {
  font-weight: 400;
  color: var(--accent-dark);
}

.voice-row .btn.is-selected {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.wizard-step .voice-warning,
.wizard-step .voice-note {
  font-weight: 400;
  font-size: 0.85rem;
  padding: 6px 10px;
  border-radius: var(--radius);
}

.wizard-step .voice-warning {
  background: var(--warn-soft);
  border: 1px solid var(--warn-line);
  color: var(--warn);
}

.wizard-step .voice-note {
  background: var(--fill-soft);
  color: var(--muted);
}

.wizard-close {
  background: none;
  border: none;
  font-size: 1.4rem;
  line-height: 1;
  color: var(--faint);
  cursor: pointer;
}

.wizard-video-controls {
  display: flex;
  gap: 6px;
  margin: -6px 0 14px;
}

.btn-video-nudge {
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--ground-alt);
  color: var(--muted);
  font-size: 0.72rem;
  padding: 3px 9px;
  cursor: pointer;
}

.btn-video-nudge:hover {
  background: var(--fill);
  color: var(--ink);
}

/* Sidebar transport bar (see game_video_controls_controller.js) — a
   compact mirror of a subset of the video player's own control bar, so
   seek/frame-step/play/rate are reachable without scrolling back up to it. */
.video-transport-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

/* The play/pause button is the primary action in the row, so it gets the
   same solid treatment as the app's other primary buttons instead of the
   subtle outlined pill the rest of this row uses. */
.btn-video-play {
  width: 34px;
  height: 34px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

.btn-video-play:hover {
  background: var(--accent-dark);
  color: #fff;
}

.wizard-step p {
  font-weight: 600;
  margin: 0 0 8px;
}

.wizard-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.wizard-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-weight: 600;
  cursor: pointer;
}

/* Shot and Turnover stay as two separate labeled rows of buttons, stacked
   one above the other, rather than merging into a single flat row of four —
   keeps the same label-then-row shape as the rest of the wizard's steps. */
.wizard-type-group + .wizard-type-group {
  margin-top: 14px;
}

.card .button-row {
  margin: 10px 0;
}

.timeout-time-fields {
  margin-bottom: 10px;
}

/* Uses the browser's default top-layer centering (unlike .stat-wizard, which is
   positioned to sit inline within a specific container) since it can pop up from
   anywhere on the page and just needs to be a normal centered modal. */
.key-modal {
  border: none;
  border-radius: var(--radius-lg);
  padding: 0;
  width: 92%;
  max-width: 900px;
  max-height: 85vh;
}

.key-modal[open] {
  display: flex;
  flex-direction: column;
}

.key-modal::backdrop {
  background: rgba(0, 0, 0, 0.45);
}

.key-modal .wizard-header {
  padding: 20px 24px;
  margin-bottom: 0;
  border-bottom: 1px solid var(--line);
}

.key-modal-body {
  padding: 20px 24px 24px;
  overflow-y: auto;
}

.key-modal-body h3 {
  font-size: 0.95rem;
  margin: 20px 0 4px;
}

.key-modal-body h3:first-child {
  margin-top: 0;
}

.key-modal-body p {
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0 0 10px;
}

.key-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 8px;
}

.key-table td {
  padding: 4px 8px 4px 0;
  font-size: 0.85rem;
  vertical-align: top;
  border-bottom: 1px solid var(--line-soft);
}

.key-table td:first-child {
  font-weight: 600;
  white-space: nowrap;
  width: 1%;
}

.playing-time-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}

.playing-time-row:last-child {
  border-bottom: none;
}

.playing-time-segments {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  flex: 1;
}

/* Player page: All teams / one chip per team (players/show.html.erb). */
.team-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 12px;
}

.team-filter-option {
  background: var(--fill);
  color: var(--ink);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 0.85rem;
  text-decoration: none;
}

.team-filter-option:hover {
  background: var(--fill-strong);
}

.team-filter-option.is-active {
  background: var(--accent);
  color: #fff;
}

.segment-chip {
  background: var(--fill);
  color: var(--ink);
  border-radius: 999px;
  padding: 2px 10px;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.playing-time-total {
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--ink);
  min-width: 70px;
  text-align: right;
}

.game-video {
  position: relative;
  flex: 0 1 auto;
  margin-bottom: 16px;
  /* Fallback sizing for a video that isn't inside .video-workspace (Full
     Game mode on the finished-game page is the only place that still
     happens) — capped by whichever's the limiting dimension, full width or
     the width implied by a modest viewport-height budget, then centered
     since nothing shares the row with it there. Inside .video-workspace
     below, both of these are overridden by the shared workspace sizing. */
  width: min(100%, calc(max(100vh - 200px, 200px) * 16 / 9));
  margin-left: auto;
  margin-right: auto;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  background: #000;
}

.game-video .video-js {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* iOS won't let a YouTube video start from script, so videojs-youtube shows
   YouTube's own play button and the first tap has to land on the iframe
   itself. Video.js's loading spinner sits dead center right on top of that
   button — and since the start-time cue leaves the player "seeking" until
   playback begins, it never clears — so it swallows the tap. Hide it until
   the video has started, and never let it catch taps either way. */
.vjs-youtube-mobile .vjs-loading-spinner {
  pointer-events: none;
}

.vjs-youtube-mobile:not(.vjs-has-started) .vjs-loading-spinner {
  display: none;
}

/* Catches taps over the video on phones so they never reach YouTube's own
   links (see addTapLayer in video_player_controller.js). It lets taps
   through until the first play, which iOS insists comes from YouTube's
   player itself, and again whenever it has had to step aside. Sits just
   above the video, under the control bar and everything else. */
.vjs-tap-layer {
  position: absolute;
  inset: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.video-js:not(.vjs-has-started) .vjs-tap-layer,
.vjs-tap-layer.is-passthrough {
  pointer-events: none;
}

.vjs-seek-group {
  position: relative;
}

.vjs-seek-main-button {
  width: 100%;
  height: 100%;
  cursor: pointer;
}

.vjs-seek-options {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, 4px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: rgba(28, 28, 28, 0.95);
  border-radius: 6px;
  padding: 4px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
}

.vjs-seek-group:hover .vjs-seek-options,
.vjs-seek-group:focus-within .vjs-seek-options {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, 0);
}

.vjs-seek-option {
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #fff;
  font-size: 0.85em;
  font-family: var(--font-body);
  padding: 5px 10px;
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
}

.vjs-seek-option:hover,
.vjs-seek-option:focus {
  background: rgba(255, 255, 255, 0.15);
}

.vjs-rate-group {
  position: relative;
}

.vjs-rate-main-button {
  width: 100%;
  height: 100%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.vjs-rate-main-label {
  font-size: 0.78em;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.vjs-rate-options {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, 4px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: rgba(28, 28, 28, 0.95);
  border-radius: 6px;
  padding: 4px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
}

.vjs-rate-group:hover .vjs-rate-options,
.vjs-rate-group:focus-within .vjs-rate-options {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, 0);
}

.vjs-rate-option {
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #fff;
  font-size: 0.85em;
  font-family: var(--font-body);
  padding: 5px 10px;
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
}

.vjs-rate-option:hover,
.vjs-rate-option:focus {
  background: rgba(255, 255, 255, 0.15);
}

/* One instance sits on each side of the play button — a plain single button,
   no hover popup needed (unlike the seek/rate groups above) since there's
   only one step size. */
.vjs-frame-step-button {
  width: 100%;
  height: 100%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.vjs-frame-step-label {
  font-size: 0.68em;
  font-weight: 600;
  letter-spacing: -0.02em;
}

/* Zooms only the video (the controls and overlays stay put), with the
   origin moved to pan around — see addZoom in video_player_controller.js.
   Only the scale animates, so dragging the view around stays immediate. */
.video-js .vjs-tech {
  transform: scale(var(--zoom, 1));
  transform-origin: var(--zoom-x, 50%) var(--zoom-y, 50%);
  transition: transform 0.15s ease;
}

/* Follows the fingers directly during a pinch instead of easing behind. */
.vjs-pinching .vjs-tech {
  transition: none;
}

/* Takes drags (to pan) and taps (to play/pause) over the video while it's
   zoomed in. Kept out of the way before the first play for the same reason
   as .vjs-tap-layer above. */
.vjs-pan-layer {
  position: absolute;
  inset: 0;
  display: none;
  cursor: grab;
  touch-action: none;
  -webkit-tap-highlight-color: transparent;
}

.vjs-zoomed.vjs-has-started .vjs-pan-layer {
  display: block;
}

.vjs-pan-layer.is-panning {
  cursor: grabbing;
}

.vjs-zoom-group {
  position: relative;
}

.vjs-zoom-main-button {
  width: 100%;
  height: 100%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.vjs-zoom-icon {
  width: 1.5em;
  height: 1.5em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
}

.vjs-zoom-options {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, 4px);
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: rgba(28, 28, 28, 0.95);
  border-radius: 6px;
  padding: 4px;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
}

.vjs-zoom-group:hover .vjs-zoom-options,
.vjs-zoom-group:focus-within .vjs-zoom-options {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, 0);
}

.vjs-zoom-option {
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #fff;
  font-size: 0.85em;
  font-family: var(--font-body);
  padding: 5px 10px;
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
}

.vjs-zoom-option:hover,
.vjs-zoom-option:focus {
  background: rgba(255, 255, 255, 0.15);
}

/* The default skin shows only remaining time; show elapsed time and the
   total duration instead, e.g. "3:12 / 15:00". */
.video-js .vjs-current-time,
.video-js .vjs-duration,
.video-js .vjs-time-divider {
  display: block;
}

.video-js .vjs-remaining-time {
  display: none;
}

/* On phones (videojs-youtube tags the player .vjs-youtube-mobile on iOS and
   Android) the full bar is too wide, and fullscreen — the last button —
   gets clipped off the end. Volume goes since phones use their hardware
   buttons (and iOS ignores page volume anyway), the total duration since
   elapsed time alone is enough there, and play/pause since tapping the
   video itself already does that on a phone. */
.vjs-youtube-mobile .vjs-play-control,
.vjs-youtube-mobile .vjs-volume-panel,
.vjs-youtube-mobile .vjs-time-divider,
.vjs-youtube-mobile .vjs-duration {
  display: none;
}

/* A quieter, more minimal player skin: a frosted control bar instead of a
   flat gray bar, a circular play button instead of the stock rounded-rect
   one, the site's own green as the "played" accent instead of plain white,
   and no default text-shadow hover glow. */
.video-js {
  font-family: var(--font-body);
}

.video-js .vjs-control-bar {
  background: rgba(15, 17, 15, 0.45);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.video-js .vjs-big-play-button {
  width: 64px;
  height: 64px;
  line-height: 64px;
  margin-top: -32px;
  margin-left: -32px;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  background-color: rgba(15, 17, 15, 0.5);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 4px 18px rgba(0, 0, 0, 0.25);
  transition: transform 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
}

.video-js .vjs-big-play-button .vjs-icon-placeholder:before {
  font-size: 24px;
}

.video-js:hover .vjs-big-play-button,
.video-js .vjs-big-play-button:focus {
  background-color: rgba(31, 91, 143, 0.75);
  border-color: rgba(255, 255, 255, 0.6);
  transform: scale(1.05);
  transition: transform 0.15s ease, background-color 0s, border-color 0s;
}

.video-js .vjs-control:focus:before,
.video-js .vjs-control:hover:before,
.video-js .vjs-control:focus {
  text-shadow: none;
}

.video-js .vjs-control:hover,
.video-js .vjs-control:focus {
  opacity: 0.8;
}

/* The seek group's own hover/focus opacity is skipped so the popup above it
   (a child, and therefore also dimmed by the parent's opacity) stays crisp;
   the main button gets the same dimmed hover feedback on its own instead. */
.video-js .vjs-seek-group:hover,
.video-js .vjs-seek-group:focus-within {
  opacity: 1;
}

.video-js .vjs-seek-main-button:hover,
.video-js .vjs-seek-main-button:focus {
  opacity: 0.8;
}

.video-js .vjs-rate-group:hover,
.video-js .vjs-rate-group:focus-within {
  opacity: 1;
}

.video-js .vjs-rate-main-button:hover,
.video-js .vjs-rate-main-button:focus {
  opacity: 0.8;
}

.video-js .vjs-zoom-group:hover,
.video-js .vjs-zoom-group:focus-within {
  opacity: 1;
}

.video-js .vjs-zoom-main-button:hover,
.video-js .vjs-zoom-main-button:focus {
  opacity: 0.8;
}

.video-js .vjs-slider {
  background-color: rgba(255, 255, 255, 0.2);
}

/* No overflow: hidden here — the hover time tooltip (.vjs-mouse-display,
   below) is an absolutely-positioned child that pops up above this bar, and
   clipping the holder would clip that right along with the fill bars. The
   pill look comes from rounding each fill bar's own corners instead. */
.video-js .vjs-progress-holder {
  height: 0.35em;
  border-radius: 999px;
}

.video-js .vjs-load-progress {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
}

.video-js .vjs-load-progress div {
  background: rgba(255, 255, 255, 0.28);
  border-radius: 999px;
}

.video-js .vjs-play-progress {
  background-color: var(--accent-bright);
  border-radius: 999px;
}

.video-js .vjs-play-progress:before {
  display: none;
}

.video-js .vjs-volume-level {
  background-color: var(--accent-bright);
}

.video-js .vjs-volume-level:before {
  display: none;
}

.video-js .vjs-mouse-display .vjs-time-tooltip {
  background-color: rgba(28, 28, 28, 0.95);
}

.video-js .vjs-time-tooltip {
  background-color: rgba(28, 28, 28, 0.95);
  color: #fff;
  border-radius: 6px;
  font-family: var(--font-body);
}

.vjs-seeking .vjs-loading-spinner:before,
.vjs-seeking .vjs-loading-spinner:after,
.vjs-waiting .vjs-loading-spinner:before,
.vjs-waiting .vjs-loading-spinner:after {
  border-top-color: var(--accent-bright);
}

/* Reserves room below the workspace for it to pin into once you scroll
   past it (see .workspace-pinned below) — without this, once pinned there
   would be nowhere left to scroll down into, and the video could never
   actually reach the top of the screen (the page would already be at its
   bottom the moment the pin engages). A flat constant rather than
   something computed exactly to the workspace's height is deliberately
   generous: a little extra blank scroll room at the bottom costs nothing,
   where too little would cap out the scroll distance before the video
   finished sliding up to top: 16px. */
.video-workspace-active {
  min-height: calc(100vh - 16px);
}

/* Zero-height marker sitting right where the workspace naturally starts —
   video_pin_controller.js's IntersectionObserver views this, not the
   workspace itself, since once pinned the workspace is removed from flow
   and can't meaningfully report "have I scrolled past my own original
   position" anymore. */
.video-workspace-sentinel {
  height: 0;
}

/* The video+stats/actions workspace shared by the game show page
   (progressive mode only — see progressive_stats_controller.js, which
   toggles .video-workspace-active; Full Game mode is a plain block, video
   on top, normal page scroll), live stat tracking, and replay/edit (both
   of which have .video-workspace-active set unconditionally in the view,
   since there's no alternate mode to fall back to).
   Video and stats/actions sit side by side (stacked below 1024px). The
   workspace starts at its normal in-flow position (right below the title)
   and pins to the top of the viewport once scrolled past — like a sticky
   header — so the video stays reachable without pausing or scrolling it
   away, while the content pane scrolls internally within its own bounded
   height instead of pushing the video off screen. Buttons come first in
   that pane's markup on every page that uses this (player action rows,
   the replay edit list), ahead of any secondary read-only stats tables —
   so they're what's immediately visible without scrolling, with the
   tables reachable by scrolling further within the same pane.
   The "start in flow, then pin" transition is driven by
   video_pin_controller.js toggling .workspace-pinned via an
   IntersectionObserver — not position: sticky, which computes a valid
   engage range here but never actually engages; something in this app
   silently breaks it even for a bare, class-free div with nothing else
   applied, verified with a from-scratch isolated build/serve of the same
   markup. Everything about *how* it's positioned once pinned (or not) is
   plain CSS below; JS only flips the one class.
   .game-video is a *direct* flex child here (not wrapped in another div) —
   its own width formula below resolves percentages against this flex
   container's width; wrapping it in an extra auto-sized div would make
   that percentage circular and collapse the video to 0x0.
   align-items is left at its default (stretch) deliberately: the content
   pane needs to stretch to the row's full (max-height-bounded) height so
   its own overflow-y:auto below has a bounded height to scroll within.
   .game-video opts back out of that via align-self below — despite having
   an explicit width + aspect-ratio, Chromium still stretches its height
   past the aspect-ratio-implied value here, distorting the video. */
.video-workspace-active .video-workspace {
  display: flex;
  gap: 16px;
  /* Never taller than the viewport, pinned or not. */
  max-height: calc(100vh - 32px);
  /* How much of that max-height the video is allowed to claim — the rest
     goes to the content pane. 1 (all of it) side by side, since the pane's
     width comes from flex there instead; a media query below lowers it
     once video and pane are stacked and have to share vertical space
     too. */
  --video-height-fraction: 1;
}

/* Both classes land on the same element (see video_pin_controller.js), so
   this only matches once actually scrolled past — an unpinned workspace
   stays in normal flow and needs none of this. The left/right inset
   replicates .container's own side padding, which fixed positioning
   otherwise ignores. */
.video-workspace-active.workspace-pinned .video-workspace {
  position: fixed;
  top: calc(16px + var(--safe-top));
  left: 16px;
  right: 16px;
  z-index: 2;
}

.video-workspace-active .video-workspace > .game-video {
  align-self: flex-start;
  /* Never taller than the available height budget above, and — side by
     side — never wider than 70% of the row (the rest belongs to the
     content pane); whichever of those two caps produces the smaller width
     wins, so it's always exactly one or the other that's the limiting
     dimension. */
  width: min(70%, calc((100vh - 32px) * var(--video-height-fraction) * 16 / 9));
  /* .game-video's base rule (flex: 0 1 auto) allows it to shrink, which in
     the stacked/column layout below lets the flex algorithm squeeze it well
     under its width-formula-derived height to make room for the content
     pane's flex-grow. The content pane is the one meant to flex; the video
     should always render at exactly its formula-derived size. */
  flex-shrink: 0;
  margin-left: 0;
  margin-right: 0;
}

.video-workspace-active .video-workspace-scroll {
  flex: 1 1 0%;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
}

/* 1360px rather than a rounder number lower down: below that, a 30%-width
   content pane next to the video doesn't leave the roster's player-name
   column (see .player-name) enough room to show a typical name in full
   without truncating — stacking to full width instead avoids that squeeze
   entirely rather than trying to survive it. */
@media (max-width: 1359px) {
  .video-workspace-active .video-workspace {
    flex-direction: column;
    /* Video and the content pane now stack and split the same vertical
       budget instead of sitting in separate columns, so the video can't
       claim all of it — leave real room for the pane below. */
    --video-height-fraction: 0.55;
  }

  .video-workspace-active .video-workspace > .game-video {
    /* Full width is the relevant cap now, not 70% of a row it's no longer
       sharing horizontally with the content pane. */
    width: min(100%, calc((100vh - 32px) * var(--video-height-fraction) * 16 / 9));
    /* Centered horizontally whenever the height cap (rather than the width
       cap) ends up being the limiting factor and it's narrower than the
       column. */
    align-self: center;
  }

  .video-workspace-active .video-workspace-scroll {
    width: 100%;
  }
}

/* --- The stats panel (phones, progressive mode) ---
   On phones the pane under the video becomes a panel headed by the score
   and Stats | Highlight reel, filling the screen under the pinned video
   (see stats_sheet_controller.js). Anywhere else the panel's wrappers lay
   out as if they weren't there, leaving the tabs sticky in the pane. */
.stats-sheet-head,
.stats-sheet-bar {
  display: contents;
}

.stats-sheet-handle,
.sheet-score,
.stat-tabs-select {
  display: none;
}

.stat-tabs-select {
  min-height: 42px;
  margin-top: 4px;
  padding: 0 12px;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
}

/* (.game-video's, so the video player's own styles for buttons inside it
   don't show it.) */
.game-video .theater-toggle,
.theater-score,
.theater-stats {
  display: none;
}

/* Phones held upright: the page itself. */
@media (max-width: 600px) {
  /* The title's line under it keeps to one line; the video's start and
     end times are for whoever's editing, on a bigger screen. */
  .game-context {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .game-context .meta-info {
    display: none;
  }

  /* Tapping the score switches instead (see .sheet-score). */
  .page-actions .stats-view-switch-link {
    display: none;
  }

  /* One page scroll instead of a pane scrolling inside it: the video pins,
     then the panel under it. */
  .game-watch.video-workspace-active .video-workspace {
    display: block;
    max-height: none;
  }

  .game-watch.video-workspace-active .video-workspace > .game-video {
    width: 100%;
  }

  .game-watch.video-workspace-active.workspace-pinned .video-workspace {
    position: static;
    /* Holds the pinned video's place: its height plus its margin. */
    padding-top: calc((100vw - 32px) * 9 / 16 + 16px);
  }

  .game-watch.video-workspace-active.workspace-pinned .video-workspace > .game-video {
    position: fixed;
    top: calc(16px + var(--safe-top));
    left: 16px;
    right: 16px;
    width: auto;
    z-index: 3;
  }

  .game-watch.video-workspace-active .video-workspace-scroll {
    overflow: visible;
  }

  /* Sized to fill the screen under the pinned video (16px above it, 12px
     below), so once scrolled there it pins too; dvh so the browser's own
     toolbar doesn't hide its bottom. */
  .video-workspace-active .stats-sheet {
    height: calc(100vh - (100vw - 32px) * 9 / 16 - 28px - var(--safe-top) - var(--safe-bottom));
    height: calc(100dvh - (100vw - 32px) * 9 / 16 - 28px - var(--safe-top) - var(--safe-bottom));
    margin-top: 12px;
    border: 1px solid var(--line);
    border-radius: 8px;
    overflow: hidden;
  }

  .video-workspace-active.workspace-pinned .stats-sheet {
    position: fixed;
    top: calc(16px + var(--safe-top) + (100vw - 32px) * 9 / 16 + 12px);
    bottom: calc(12px + var(--safe-bottom));
    left: 16px;
    right: 16px;
    z-index: 3;
    height: auto;
    margin: 0;
  }

  /* Until then, swiping it scrolls the page there rather than the stats. */
  .video-workspace-active:not(.workspace-pinned) .stats-sheet-body {
    overflow-y: hidden;
  }

  .video-workspace-active .theater-toggle {
    display: flex;
  }
}

/* Phones, upright or turned sideways: the stats panel's insides. */
@media (max-width: 600px), (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  /* The panel's head shows the score instead. */
  .video-workspace-active [data-progressive-stats-target="content"] > .card:first-child {
    display: none;
  }

  /* Full screen is the page's own (see theater_controller.js). */
  .game-watch.video-workspace-active .vjs-fullscreen-control {
    display: none;
  }

  .video-workspace-active .stats-sheet {
    display: flex;
    flex-direction: column;
    background: var(--surface);
  }

  .video-workspace-active .stats-sheet-head {
    display: flex;
    flex-direction: column;
    flex: none;
    padding: 0 16px;
  }

  .video-workspace-active .stats-sheet-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 46px;
  }

  .video-workspace-active .sheet-score {
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    padding: 0;
    border: none;
    background: none;
    color: var(--faint);
    font: inherit;
    cursor: pointer;
  }

  .sheet-score-final,
  .sheet-score-watch {
    display: none;
  }

  .sheet-score-mode {
    margin-right: 2px;
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent);
  }

  .sheet-score-for,
  .sheet-score-against {
    font-family: var(--font-display);
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  .sheet-score-for {
    color: var(--win);
  }

  .sheet-score-against {
    color: var(--loss);
  }

  .sheet-score-dash {
    font-size: 1.1rem;
  }

  /* Stats | Highlight reel, as a compact switch beside the score. */
  .video-workspace-active .stats-sheet .reel-tabs {
    position: static;
    margin-left: auto;
    gap: 2px;
    padding: 3px;
    border: 1px solid var(--line);
    border-radius: 5px;
    background: var(--fill);
  }

  .video-workspace-active .stats-sheet .reel-tab {
    flex: none;
    min-height: 34px;
    padding: 0 12px;
    border: none;
    border-radius: 3px;
    background: none;
    color: var(--muted);
    font-size: 0.85rem;
    font-weight: 600;
  }

  .video-workspace-active .stats-sheet .reel-tab.is-active {
    background: var(--surface);
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(26, 26, 26, 0.12);
  }

  .video-workspace-active .stats-sheet-body {
    flex: 1;
    min-height: 0;
    padding: 0 16px 16px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .video-workspace-active .stats-sheet .stats-section {
    padding: 0;
    border: none;
    background: none;
  }

  .video-workspace-active .stats-sheet .stat-tabs-nav.primary {
    display: none;
  }

  .video-workspace-active .stats-sheet .stat-tabs-select {
    display: block;
  }

  /* The video's button for watching full screen, and back. */
  .theater-toggle {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 2;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: none;
    border-radius: 999px;
    background: rgba(15, 17, 15, 0.45);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    color: #fff;
    cursor: pointer;
  }

  .theater-toggle-exit,
  .is-theater .theater-toggle-enter {
    display: none;
  }

  .is-theater .theater-toggle-exit {
    display: block;
  }

  .theater-score {
    color: #fff;
    text-align: center;
  }

  .theater-score-mode {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent-bright);
  }

  .theater-score-team {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 0;
  }

  .theater-score-points {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  /* Lighter than the page's own green and red, to read on black. */
  .theater-score-points.for {
    color: #7fd9a4;
  }

  .theater-score-points.against {
    color: #ff9c9c;
  }

  .theater-score-name {
    max-width: 100%;
    margin-top: 4px;
    font-size: 0.75rem;
    line-height: 1.2;
    color: rgba(255, 255, 255, 0.7);
  }

  .theater-score-dash {
    color: rgba(255, 255, 255, 0.4);
  }
}

/* Phones held upright, full screen (see theater_controller.js): the score
   above the video, and the stats panel under it as on the page. */
@media (max-width: 600px) {
  /* Full screen covers the whole page, so it shouldn't scroll behind. */
  html:has(.video-workspace-active.is-theater) {
    overflow: hidden;
  }

  .video-workspace-active.is-theater {
    --theater-top: calc(120px + var(--safe-top));
  }

  .video-workspace-active.is-theater .video-workspace {
    position: fixed;
    inset: 0;
    z-index: 50;
    padding: 0;
    background: #000;
  }

  /* The score's already showing, bigger. */
  .video-workspace-active.is-theater .score-flash {
    display: none;
  }

  .video-workspace-active.is-theater .video-workspace > .game-video {
    position: absolute;
    top: var(--theater-top);
    left: 0;
    right: 0;
    width: 100%;
    margin: 0;
    border-radius: 0;
    box-shadow: none;
  }

  .video-workspace-active.is-theater .theater-score {
    display: flex;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: var(--theater-top);
    align-items: center;
    justify-content: center;
    gap: 16px;
    padding: calc(18px + var(--safe-top)) 16px 0;
  }

  .video-workspace-active.is-theater .theater-score-mode {
    position: absolute;
    top: calc(14px + var(--safe-top));
  }

  .video-workspace-active.is-theater .theater-score-team {
    width: 40%;
  }

  .video-workspace-active.is-theater .theater-score-points {
    font-size: 3rem;
  }

  .video-workspace-active.is-theater .theater-score-name {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .video-workspace-active.is-theater .theater-score-dash {
    font-size: 1.6rem;
  }

  .video-workspace-active.is-theater .stats-sheet {
    position: absolute;
    top: calc(var(--theater-top) + 100vw * 9 / 16 + 12px);
    bottom: 0;
    left: 0;
    right: 0;
    height: auto;
    margin: 0;
    border: none;
    border-radius: 14px 14px 0 0;
  }

  .video-workspace-active.is-theater .stats-sheet-body {
    overflow-y: auto;
    padding-bottom: calc(16px + var(--safe-bottom));
  }
}

/* Phones turned sideways always watch full screen: the video as big as
   it fits, the score in the black bar beside it, and a button in the
   other bar that opens the stats as a drawer, sliding the video over. */
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  /* Black too, so a Home Screen copy's sides by the notch match. The
     page behind still scrolls, unseen, so a swipe on the black bars can
     tuck the browser's address bar away. */
  html:has(.game-watch.video-workspace-active) {
    background: #000;
  }

  .video-workspace-active {
    --video-width: min(100vw, 100dvh * 16 / 9);
    --side-bar: calc((100vw - var(--video-width)) / 2);
  }

  .video-workspace-active[data-controller~="theater"] .video-workspace {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: block;
    max-height: none;
    padding: 0;
    background: #000;
  }

  .video-workspace-active[data-controller~="theater"] .video-workspace > .game-video {
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: 1;
    width: var(--video-width);
    margin: 0;
    border-radius: 0;
    box-shadow: none;
    transform: translate(-50%, -50%);
    transition: left 0.25s ease-out, transform 0.25s ease-out;
  }

  .video-workspace-active .video-workspace:has(.stats-sheet.is-up) > .game-video {
    left: 0;
    transform: translate(0, -50%);
  }

  .game-watch.video-workspace-active .video-workspace-scroll {
    overflow: visible;
  }

  .game-watch.video-workspace-active .score-flash {
    display: none;
  }

  .video-workspace-active .theater-toggle {
    display: none;
  }

  /* Only where it can actually hide the browser's bars (Android). */
  .video-workspace-active.can-fullscreen .theater-toggle {
    display: flex;
  }

  .video-workspace-active .theater-score {
    display: flex;
    position: absolute;
    top: 0;
    bottom: 0;
    /* Clear of the notch, turned that way. */
    left: env(safe-area-inset-left, 0px);
    width: calc(var(--side-bar) - env(safe-area-inset-left, 0px));
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 0 8px;
  }

  .video-workspace-active .theater-score-points {
    font-size: 2.5rem;
  }

  .video-workspace-active .theater-score-dash {
    width: 28px;
    height: 1px;
    background: rgba(255, 255, 255, 0.2);
    font-size: 0;
  }

  .video-workspace-active .theater-stats {
    display: flex;
    position: absolute;
    top: 50%;
    right: calc((var(--side-bar) + env(safe-area-inset-right, 0px)) / 2);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 64px;
    height: 88px;
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.08);
    color: #fff;
    font: inherit;
    font-size: 0.8rem;
    font-weight: 600;
    transform: translate(50%, -50%);
    cursor: pointer;
  }

  .video-workspace-active .stats-sheet {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: auto;
    z-index: 2;
    width: max(280px, 100vw - var(--video-width));
    height: auto;
    /* Clear of the notch, turned that way. */
    padding-right: env(safe-area-inset-right, 0px);
    border-radius: 0;
    box-shadow: -4px 0 18px rgba(0, 0, 0, 0.3);
    transform: translateX(100%);
    /* Hidden once it's slid away, so nothing in it can be reached. */
    visibility: hidden;
    transition: transform 0.25s ease-out, visibility 0s 0.25s;
  }

  .video-workspace-active .stats-sheet.is-up {
    transform: none;
    visibility: visible;
    transition: transform 0.25s ease-out;
  }

  /* Its head: a close button, then the score over Stats | Highlight reel. */
  .video-workspace-active .stats-sheet-head {
    flex-direction: row;
    align-items: center;
    gap: 4px;
    height: auto;
    padding: 8px 12px 8px 4px;
  }

  .video-workspace-active .stats-sheet-handle {
    display: block;
    position: relative;
    width: 36px;
    height: 44px;
    flex: none;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
  }

  .video-workspace-active .stats-sheet-handle::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    right: auto;
    width: 9px;
    height: 9px;
    border-right: 2px solid var(--muted);
    border-bottom: 2px solid var(--muted);
    border-radius: 0;
    background: none;
    transform: translate(-70%, -50%) rotate(-45deg);
  }

  .video-workspace-active .stats-sheet-bar {
    flex: 1;
    flex-wrap: wrap;
    gap: 4px 10px;
    height: auto;
  }

  .video-workspace-active .stats-sheet .reel-tabs {
    flex: 1 0 100%;
    margin-left: 0;
  }

  .video-workspace-active .stats-sheet .reel-tab {
    flex: 1;
  }
}

/* Phones, Full Game mode: the final score and the tables first, then the
   video under them, all one page as before. */
@media (max-width: 600px) {
  div:not(.video-workspace-active) > .video-workspace {
    display: flex;
    flex-direction: column;
  }

  div:not(.video-workspace-active) > .video-workspace > .game-video {
    order: 1;
    width: 100%;
    margin: 16px 0 0;
  }

  /* The score strip below shows it instead. */
  div:not(.video-workspace-active) [data-progressive-stats-target="content"] > .card:first-child {
    display: none;
  }

  div:not(.video-workspace-active) > .video-workspace .sheet-score {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    min-height: 56px;
    margin-bottom: 12px;
    padding: 8px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--faint);
    font: inherit;
    text-align: left;
  }

  button.sheet-score {
    cursor: pointer;
  }

  div:not(.video-workspace-active) > .video-workspace .sheet-score-live,
  div:not(.video-workspace-active) > .video-workspace .sheet-score-swap {
    display: none;
  }

  div:not(.video-workspace-active) > .video-workspace .sheet-score-final {
    display: inline;
    order: 1;
    margin: 0 0 0 4px;
    color: var(--muted);
  }

  div:not(.video-workspace-active) > .video-workspace .sheet-score-watch {
    display: flex;
    align-items: center;
    gap: 6px;
    order: 2;
    margin-left: auto;
    color: var(--accent);
    font-size: 0.85rem;
    font-weight: 600;
  }

  /* Each row of tabs is a dropdown, the inner one (Totals, Per 20…)
     beside the outer one (Box Score, On Court…). */
  .stats-sheet .stat-tabs-nav {
    display: none;
  }

  /* Half the width each, since a dropdown otherwise sizes itself to its
     longest choice. */
  .stats-sheet .stat-tabs-select {
    display: inline-block;
    max-width: calc(50% - 4px);
  }

  /* Not where a Team | Opp switch shares the inner one's row (see
     teams/_sided_stat_views): that row goes under it instead. */
  .stats-sheet .stat-tabs-select:has(~ .stat-tab-panel:not([hidden]) > .stat-tabs > .stat-tabs-select) {
    float: left;
    margin-right: 8px;
  }
}

/* Too little room beside the video for the score to stand in a bar clear
   of the notch (see theater_controller.js): it rides in the video's
   corner instead, with the stats button beside it. */
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  .video-workspace-active:not(.score-in-bar) .theater-score {
    top: 8px;
    bottom: auto;
    left: calc(8px + env(safe-area-inset-left, 0px));
    z-index: 2;
    width: auto;
    flex-direction: row;
    gap: 6px;
    padding: 5px 12px;
    border-radius: 999px;
    background: rgba(12, 14, 16, 0.8);
  }

  .video-workspace-active:not(.score-in-bar) .theater-score-points {
    font-size: 1.15rem;
  }

  .video-workspace-active:not(.score-in-bar) .theater-score-name {
    display: none;
  }

  .video-workspace-active:not(.score-in-bar) .theater-score-dash {
    width: auto;
    height: auto;
    background: none;
    font-size: 0.9rem;
  }

  .video-workspace-active:not(.score-in-bar) .theater-stats {
    top: 8px;
    right: calc(8px + env(safe-area-inset-right, 0px));
    z-index: 2;
    flex-direction: row;
    width: auto;
    height: 34px;
    padding: 0 12px;
    border-radius: 999px;
    background: rgba(12, 14, 16, 0.8);
    transform: none;
  }

  /* Clear of the video's own full screen button. */
  .video-workspace-active.can-fullscreen:not(.score-in-bar) .theater-stats {
    top: 52px;
  }
}

.game-stats {
  /* Positioned so the stat wizard and other in-page dialogs (rendered with
     dialog.show(), not showModal()) can be absolutely positioned to fill
     just this element rather than the browser's top layer — see .stat-wizard. */
  position: relative;
}

/* --- Stat tracking on phones (games/show.html.erb while the game's live) ---
   The stats pane becomes one panel: the video's buttons and a score strip
   fixed at its top, the last stat ready to undo fixed at its bottom, and
   the roster scrolling between them. The wizard and the other dialogs fill
   just this element (see .stat-wizard), so on phones they replace the
   whole panel and leave the video in view. Upright the panel sits under
   the pinned video; turned sideways it's a column beside it. Anywhere
   else the phone-only pieces stay hidden and the page is as it was. */
.tracking-strip,
.tracking-start,
.tracking-undo,
.tracking-voice-status,
.label-short,
.start-video-hint-short {
  display: none;
}

.start-lineup-count {
  margin-left: 6px;
  white-space: nowrap;
  color: var(--accent);
  font-weight: 600;
}

@media (max-width: 600px), (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  .tracking-workspace .game-stats {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--surface);
  }

  .tracking-workspace .game-stats > .card,
  .tracking-pane > .card {
    margin: 0;
    padding: 8px 14px;
    border: none;
    border-radius: 0;
  }

  .tracking-workspace .game-stats > .flash {
    margin: 8px 14px 0;
  }

  /* All behind the strip's ⋯ menu instead, or (the score) in the strip. */
  .tracking-workspace .game-actions-card,
  .tracking-workspace .scoreboard-card,
  .tracking-workspace .warning-banner {
    display: none;
  }

  .tracking-pane {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  .tracking-pane > .card + .card {
    border-top: 1px solid var(--line);
  }

  /* The video's buttons, thumb-sized. */
  .tracking-workspace .video-transport-controls {
    justify-content: space-between;
    gap: 4px;
  }

  .tracking-workspace .video-transport-controls .btn-video-nudge {
    min-width: 44px;
    height: 44px;
    padding: 0 8px;
    font-size: 0.85rem;
    font-weight: 600;
  }

  .tracking-workspace .video-transport-controls .btn-video-play {
    width: 52px;
    height: 48px;
    font-size: 1rem;
  }

  .tracking-strip {
    position: relative;
    display: flex;
    align-items: center;
    gap: 4px;
    flex: none;
    padding: 2px 4px 2px 14px;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
  }

  .tracking-score {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
    min-height: 48px;
    padding: 0;
    border: none;
    background: none;
    color: var(--ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
  }

  .tracking-score:disabled {
    cursor: default;
  }

  .tracking-score-for,
  .tracking-score-against {
    font-family: var(--font-display);
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }

  .tracking-score-for {
    color: var(--win);
  }

  .tracking-score-against {
    color: var(--loss);
  }

  .tracking-score-dash {
    color: var(--faint);
  }

  /* Beside whichever team has the ball (see possession_controller.js). */
  .tracking-possession {
    width: 8px;
    height: 8px;
    flex: none;
    border-radius: 50%;
    background: #d38b00;
  }

  .game-stats:not(.possession-defense) .tracking-possession-against,
  .game-stats.possession-defense .tracking-possession-for {
    visibility: hidden;
  }

  .tracking-teams {
    min-width: 0;
    margin-left: 4px;
    overflow: hidden;
    color: var(--muted);
    font-size: 0.75rem;
    line-height: 1.25;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .tracking-menu-button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius);
    color: var(--ink);
    cursor: pointer;
  }

  .tracking-menu[open] > .tracking-menu-button {
    background: var(--fill);
  }

  /* Voice entry's mic, used every play, so in the strip rather than behind
     ⋯ — red while it's listening, like the desktop Voice button. */
  .tracking-voice-button {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 1px solid var(--accent);
    border-radius: 50%;
    background: var(--surface);
    color: var(--accent);
    cursor: pointer;
  }

  .tracking-voice-button:disabled {
    opacity: 0.35;
  }

  .is-listening .tracking-voice-button {
    border-color: var(--danger);
    background: var(--danger);
    color: #fff;
  }

  .tracking-voice-status:not([hidden]) {
    display: block;
    flex: none;
    margin: 0;
    padding: 6px 14px;
    border-bottom: 1px solid var(--line);
  }

  .tracking-menu-panel {
    left: auto;
    right: 0;
    top: calc(100% + 2px);
    min-width: 210px;
  }

  .tracking-menu-panel button {
    min-height: 44px;
    font-size: 1rem;
    font-weight: 600;
  }

  .tracking-menu-panel .tracking-menu-finish {
    color: var(--danger);
  }

  .tracking-start {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 16px 14px;
    border-bottom: 1px solid var(--line);
    background: var(--warn-soft);
  }

  .tracking-start p {
    margin: 0;
    color: var(--warn);
  }

  .tracking-start .btn {
    min-height: 48px;
    font-size: 1.05rem;
    font-weight: 700;
  }

  /* The roster: the name first, then its buttons, all thumb-sized. */
  .tracking-workspace .player-row {
    gap: 6px;
    padding: 6px 0;
  }

  .tracking-workspace .player-name {
    order: -1;
    font-size: 1.02rem;
  }

  .tracking-workspace .player-action-group,
  .tracking-workspace .player-sub-group {
    width: auto;
    padding: 0;
    border: none;
  }

  .tracking-workspace .player-row.benched .player-action-group {
    display: none;
  }

  .tracking-workspace .player-actions .btn,
  .tracking-workspace .player-sub-group .btn {
    min-height: 44px;
    padding: 0 12px;
    font-size: 0.95rem;
    font-weight: 600;
  }

  .tracking-workspace .roster-header-row .name-column {
    display: none;
  }

  .tracking-workspace .player-action-label,
  .tracking-workspace .player-sub-label {
    width: auto;
    padding: 0;
    border: none;
  }

  .tracking-workspace .player-action-label {
    flex: 1;
  }

  .tracking-workspace .selection-banner {
    margin: 0 0 8px;
  }

  .tracking-undo {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: none;
    min-height: 56px;
    padding: 6px 8px 6px 14px;
    border-top: 1px solid var(--line);
    background: var(--ground-alt);
  }

  .tracking-undo-text {
    flex: 1;
    min-width: 0;
    color: var(--muted);
    font-size: 0.85rem;
    line-height: 1.3;
  }

  .tracking-undo-text strong {
    color: var(--ink);
  }

  .tracking-undo .tracking-undo-button {
    min-height: 44px;
    padding: 0 16px;
    font-weight: 600;
  }

  /* The wizard and the other dialogs, filling the panel. */
  .tracking-workspace .stat-wizard {
    background: var(--surface);
  }

  .tracking-workspace .stat-wizard[open] > form {
    height: 100%;
  }

  .tracking-workspace .wizard-card {
    height: 100%;
    padding: 10px 14px 16px;
    border-radius: 0;
  }

  .tracking-workspace .wizard-header {
    margin-bottom: 10px;
  }

  .tracking-workspace .wizard-back,
  .tracking-workspace .wizard-close {
    min-width: 44px;
    min-height: 44px;
  }

  .tracking-workspace .wizard-heading {
    font-size: 1.05rem;
  }

  .tracking-workspace .wizard-video-controls {
    margin: 0 0 12px;
  }

  .tracking-workspace .wizard-video-controls .btn-video-nudge {
    flex: 1;
    min-height: 40px;
    font-size: 0.85rem;
    font-weight: 600;
  }

  .tracking-workspace .stat-wizard .wizard-options {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .tracking-workspace .stat-wizard .wizard-options .btn {
    min-height: 52px;
    font-size: 1.05rem;
    font-weight: 700;
  }

  /* The voice card asks everything at once, so its rows fit as many
     answers side by side as they can. */
  .tracking-workspace .stat-wizard .voice-row .wizard-options {
    grid-template-columns: repeat(auto-fit, minmax(84px, 1fr));
  }

  .tracking-workspace .stat-wizard .voice-row .btn {
    min-height: 44px;
    font-size: 0.95rem;
  }

  .tracking-workspace .wizard-checkbox {
    min-height: 44px;
    margin-top: 4px;
  }

  /* Phones zoom in on any field with smaller text when it's tapped. */
  .tracking-workspace input,
  .tracking-workspace select,
  .tracking-workspace textarea {
    font-size: 16px;
  }

  /* Start Game's choices as big chips. */
  [data-start-game-modal-target="dialog"] .filter-checkboxes {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  [data-start-game-modal-target="dialog"] .filter-checkbox {
    display: flex;
    align-items: center;
    margin: 0;
    min-height: 48px;
    padding: 0 12px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-lg);
    font-weight: 600;
  }

  [data-start-game-modal-target="dialog"] .filter-checkbox:has(input:checked) {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent-dark);
    box-shadow: inset 0 0 0 1px var(--accent);
  }

  [data-start-game-modal-target="dialog"] .filter-checkbox:has(input:disabled) {
    opacity: 0.5;
  }

  [data-start-game-modal-target="dialog"] .filter-checkbox input {
    width: 18px;
    height: 18px;
    margin: 0;
  }

  .tracking-workspace .start-video-hint {
    display: none;
  }

  .tracking-workspace .start-video-hint-short {
    display: block;
  }
}

/* Upright: the panel fills the screen under the pinned video. Until it's
   pinned, swiping the panel scrolls the page there rather than the roster. */
@media (max-width: 600px) {
  .tracking-workspace .video-workspace {
    gap: 10px;
    height: calc(100vh - 32px - var(--safe-top) - var(--safe-bottom));
    height: calc(100dvh - 32px - var(--safe-top) - var(--safe-bottom));
    max-height: none;
  }

  .tracking-workspace .game-stats {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
  }

  .tracking-workspace:not(.workspace-pinned) .tracking-pane {
    overflow-y: hidden;
  }
}

/* Sideways: the video as big as fits beside a column for the panel. The
   page behind still scrolls, unseen, so a swipe on the video's black edges
   can tuck the browser's address bar away. */
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  html:has(.tracking-workspace) {
    background: #000;
  }

  .tracking-workspace {
    --panel-width: 300px;
  }

  .video-workspace-active.tracking-workspace .video-workspace {
    position: fixed;
    inset: 0;
    z-index: 50;
    flex-direction: row;
    gap: 0;
    max-height: none;
    padding-left: env(safe-area-inset-left, 0px);
    background: #000;
  }

  .tracking-workspace .video-workspace > .game-video {
    width: min(100vw - var(--panel-width) - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px), 100dvh * 16 / 9);
    margin: auto;
    border-radius: 0;
    box-shadow: none;
  }

  .tracking-workspace .game-stats {
    flex: none;
    width: calc(var(--panel-width) + env(safe-area-inset-right, 0px));
    height: 100vh;
    height: 100dvh;
    padding-right: env(safe-area-inset-right, 0px);
  }

  .tracking-workspace .game-stats > .card {
    padding: 6px 10px;
  }

  .tracking-workspace .video-transport-controls .btn-video-nudge {
    min-width: 40px;
    padding: 0 6px;
  }

  .tracking-workspace .video-transport-controls .btn-video-play {
    width: 46px;
  }

  .tracking-workspace .label-long {
    display: none;
  }

  .tracking-workspace .label-short {
    display: inline;
  }
}

/* --- Highlight reel (games/_highlight_reel.html.erb, highlight_reel_controller.js) ---
   Lives as a tab in the progressive stats pane, and only there: Full Game
   mode (no .video-workspace-active) drops the tabs and the builder and
   always shows the stats, whichever tab was last picked. */
[data-controller~="highlight-reel"]:not(.video-workspace-active) .reel-tabs,
[data-controller~="highlight-reel"]:not(.video-workspace-active) .reel-panel,
[data-controller~="highlight-reel"]:not(.video-workspace-active) .reel-now-playing,
[data-controller~="highlight-reel"]:not(.video-workspace-active) .reel-caption {
  display: none;
}

[data-controller~="highlight-reel"]:not(.video-workspace-active) .reel-stats-panel[hidden] {
  display: block;
}

.reel-tabs {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  gap: 6px;
  padding-bottom: 10px;
  background: var(--ground);
}

.reel-tab {
  flex: 1;
  min-height: 42px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

.reel-tab.is-active {
  border-color: var(--accent);
  background: var(--accent);
  color: #fff;
}

.reel-panel,
.reel-now-playing {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
  border-radius: var(--radius-lg);
  background: #15181d;
  color: #fff;
}

.reel-panel[hidden],
.reel-now-playing[hidden] {
  display: none;
}

/* Everything below sizes itself to the pane, not the window: the pane is
   narrow beside the video on wide screens and full width under it on
   narrower ones (see .video-workspace). */
.reel-panel {
  container: reel / inline-size;
}

.reel-now-playing {
  margin-bottom: 12px;
}

.reel-now-playing-title {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.reel-now-playing-title strong {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.reel-now-playing-title span {
  color: #b8bdc6;
  font-size: 0.85rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.reel-now-playing-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.reel-btn {
  min-height: 40px;
  padding: 0 14px;
  border: 1px solid #3a3f47;
  border-radius: 999px;
  background: transparent;
  color: #fff;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
}

.reel-btn-quiet {
  border-color: #2a2f37;
  background: #2a2f37;
}

.reel-btn-small {
  min-height: 32px;
  padding: 0 12px;
  font-size: 0.8rem;
}

.reel-header {
  display: flex;
  align-items: flex-end;
  gap: 10px;
}

.reel-name {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.reel-eyebrow {
  color: var(--accent-bright);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.reel-name input {
  width: 100%;
  padding: 2px 0;
  border: none;
  border-bottom: 1.5px dashed #3a3f47;
  background: transparent;
  color: #fff;
  font: inherit;
  font-size: 1.2rem;
  font-weight: 800;
  outline: none;
}

.reel-name input::placeholder {
  color: #8a919c;
}

.reel-name input:focus {
  border-bottom-color: var(--accent-bright);
}

.reel-play {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 18px;
  border: none;
  border-radius: 999px;
  background: var(--accent-bright);
  color: var(--accent-on-bright);
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}

.reel-play:disabled {
  background: #4b5058;
  cursor: default;
}

.reel-timeline {
  position: relative;
  height: 22px;
  border-radius: var(--radius);
  background: #1f242c;
  overflow: hidden;
}

.reel-timeline-mini {
  height: 14px;
}

.reel-marker {
  position: absolute;
  top: 4px;
  bottom: 4px;
  border-radius: 2px;
}

.reel-marker.is-current {
  top: 0;
  bottom: 0;
}

.reel-playing .reel-marker:not(.is-current) {
  opacity: 0.45;
}

.reel-summary-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.reel-summary {
  flex: 1;
  color: #d6d9de;
  font-size: 0.85rem;
}

.reel-hint {
  margin: 0;
  color: #b8bdc6;
  font-size: 0.85rem;
  line-height: 1.45;
}

.reel-builder {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* With more players than fit, the grid scrolls sideways (play labels
   staying put) rather than squeezing its boxes below a usable size. */
.reel-grid-scroll {
  overflow-x: auto;
}

/* Narrow pane: each play's label sits on its own line above its row of
   boxes, and the column headers are initials only. */
.reel-grid {
  display: grid;
  grid-template-columns: repeat(var(--reel-columns), minmax(40px, 1fr));
  gap: 5px;
  align-items: center;
  width: max-content;
  min-width: 100%;
}

.reel-grid-corner,
.reel-column-name {
  display: none;
}

.reel-section,
.reel-row-label {
  position: sticky;
  left: 0;
  grid-column: 1 / -1;
  justify-self: start;
}

.reel-section {
  padding-top: 10px;
  color: #8a919c;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.reel-row-label {
  padding: 4px 2px 0;
  border: none;
  background: transparent;
  color: #fff;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
}

.reel-column {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 2px 0;
  border: none;
  background: transparent;
  color: #d6d9de;
  font: inherit;
  cursor: pointer;
}

.reel-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: var(--reel-color);
  color: #0b1f14;
  font-size: 0.7rem;
  font-weight: 800;
}

.reel-column-name {
  max-width: 100%;
  overflow: hidden;
  font-size: 0.75rem;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reel-cell {
  position: relative;
  height: 40px;
  border: 1.5px solid #2a2f37;
  border-radius: var(--radius);
  background: #1f242c;
  color: #8a919c;
  font: inherit;
  font-weight: 800;
  cursor: pointer;
}

.reel-cell:disabled {
  color: #4b5058;
  cursor: default;
}

.reel-cell.is-lit {
  border-color: var(--reel-color);
  background: var(--reel-color);
  color: #0b1f14;
}

/* A lit box narrowed by its own lines below the grid. */
.reel-cell.is-narrowed::after {
  content: "";
  position: absolute;
  top: 3px;
  right: 4px;
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: #0b1f14;
}

.reel-cell-gap {
  color: #3a3f47;
  text-align: center;
}

.reel-narrowing {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
  padding: 14px;
  border-radius: var(--radius-lg);
  background: #1f242c;
}

.reel-narrowing[hidden] {
  display: none;
}

.reel-narrowing-intro {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.reel-narrowing-intro span {
  color: #9aa0aa;
  font-size: 0.8rem;
}

.reel-narrow-box {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 10px;
  border-top: 1px solid #2a2f37;
}

.reel-narrow-heading {
  display: flex;
  align-items: center;
  gap: 8px;
}

.reel-narrow-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.reel-narrow-label {
  width: 100%;
  color: #9aa0aa;
  font-size: 0.75rem;
}

.reel-chip {
  min-height: 32px;
  padding: 0 11px;
  border: 1.5px solid #3a3f47;
  border-radius: 999px;
  background: transparent;
  color: #d6d9de;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
}

.reel-chip.is-on {
  border-color: var(--accent-bright);
  background: var(--accent-bright);
  color: var(--accent-on-bright);
}

.reel-dot {
  flex-shrink: 0;
  width: 10px;
  height: 10px;
  border-radius: 999px;
}

/* Wide pane (stacked under the video): labels move beside their rows,
   the column headers get names, and the narrowing sits next to the grid. */
@container reel (min-width: 600px) {
  .reel-grid {
    grid-template-columns: 8rem repeat(var(--reel-columns), minmax(48px, 4.5rem));
  }

  .reel-grid-corner,
  .reel-column-name {
    display: block;
  }

  .reel-row-label {
    grid-column: auto;
    align-self: stretch;
    padding: 0 4px;
    background: #15181d;
  }

  .reel-builder {
    flex-direction: row;
    align-items: flex-start;
  }
}

/* Over the video: what's happening in the reel's current clip, or for a
   few seconds after each play during ordinary progressive playback. Tucked
   into the corner so it covers as little of the video as possible (which
   matters most on a phone), riding just above the 30px control bar (3em of
   video.js's 10px) while that's showing and dropping down once it hides. */
.reel-caption {
  position: absolute;
  left: 8px;
  bottom: 38px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100% - 24px);
  padding: 8px 12px;
  border-radius: var(--radius-lg);
  background: rgba(15, 17, 15, 0.45);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  font-size: 0.95rem;
  font-weight: 700;
  pointer-events: none;
  transition: opacity 0.3s ease-out, bottom 0.2s ease-out;
}

.vjs-playing.vjs-user-inactive .reel-caption {
  bottom: 8px;
}

.vjs-fullscreen .reel-caption {
  left: 16px;
  bottom: 46px;
  font-size: 1.3rem;
}

.vjs-fullscreen.vjs-playing.vjs-user-inactive .reel-caption {
  bottom: 16px;
}

/* The score, flashed across the top of the video when it changes (see
   progressive_stats_controller.js). Same see-through frosted look as the
   control bar, and bigger in fullscreen like the play captions. */
.score-flash {
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: 10px;
  max-width: calc(100% - 16px);
  padding: 6px 14px;
  border-radius: var(--radius-lg);
  background: rgba(15, 17, 15, 0.45);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  font-size: 0.95rem;
  font-weight: 700;
  white-space: nowrap;
  pointer-events: none;
  transition: opacity 0.3s ease-out;
}

.vjs-fullscreen .score-flash {
  top: 16px;
  gap: 12px;
  padding: 8px 18px;
  font-size: 1.1rem;
}

.score-flash.is-fading {
  opacity: 0;
}

.score-flash[hidden] {
  display: none;
}

.score-flash-team {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.score-flash-score {
  display: inline-block;
  font-size: 1.3rem;
  font-variant-numeric: tabular-nums;
}

.vjs-fullscreen .score-flash-score {
  font-size: 1.8rem;
}

.score-flash-score.is-changed {
  animation: score-flash-pop 0.6s ease-out;
}

@keyframes score-flash-pop {
  0% { transform: scale(1.5); }
  100% { transform: scale(1); }
}

.reel-caption.is-fading {
  opacity: 0;
}

.reel-caption[hidden] {
  display: none;
}

/* Whoever's in the clip that's playing, in the progressive box score. */
tr.reel-current-row td {
  background: #fff4e0;
  font-weight: 700;
}

/* The scoreboard over a page's stats, with the Games filter
   (shared/_game_filter) and Stat Key across from it. */
.stats-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px 16px;
  margin: 12px 0 8px;
}

.stats-summary .scoreboard {
  margin-bottom: 0;
}

.stats-toolbar {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 8px 16px;
  margin-left: auto;
}

/* On its own over the player page's stats. */
.stats-toolbar:not(.stats-summary .stats-toolbar) {
  margin: 12px 0 8px;
}

.stats-toolbar .stat-key-row {
  margin: 0;
}

.game-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.game-filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 4px 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.game-filter-toggle:hover {
  border-color: var(--line-heavy);
}

.game-filter-toggle.is-active {
  border-color: var(--accent-line);
  background: var(--accent-wash);
}

.game-filter-label {
  color: var(--faint);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.game-filter-label,
.game-filter-count {
  flex: none;
}

.game-filter-count {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Wraps inside the button, rather than widening it past a phone's screen,
   when the filter has a lot to say. */
.game-filter-summary {
  min-width: 0;
  color: var(--muted);
  text-align: left;
}

.game-filter-caret {
  width: 0;
  height: 0;
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid var(--muted);
}

.game-filter-modal {
  max-width: 640px;
}

.game-filter-modal form {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.game-filter-modal .key-modal-body {
  flex: 1;
  min-height: 0;
}

.key-modal-body .game-filter-intro {
  margin-bottom: 16px;
}

.game-filter-group {
  border: none;
  padding: 0;
  margin: 0 0 16px;
  min-width: 0;
}

.game-filter-group legend {
  padding: 0;
  margin-bottom: 6px;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.game-filter-status {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  color: var(--faint);
}

.game-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0 24px;
}

.game-filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* A checkbox or radio shown as a chip, like the player page's team chips. */
.filter-chip {
  position: relative;
  cursor: pointer;
}

.filter-chip input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.filter-chip span {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--fill);
  color: var(--ink);
  font-size: 0.85rem;
}

.filter-chip:hover span {
  background: var(--fill-strong);
}

.filter-chip input:checked + span {
  background: var(--accent);
  color: #fff;
}

.filter-chip input:focus-visible + span {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.game-filter-dates {
  display: flex;
  align-items: center;
  gap: 6px;
}

.game-filter-dates input {
  padding: 5px 8px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.85rem;
}

.game-filter-dates input[type="number"] {
  width: 5em;
}

/* Under the Situation filter's score range. */
.situation-filter-note {
  margin: 6px 0 0;
  font-size: 0.8rem;
}

.game-filter-select {
  display: flex;
  gap: 14px;
  margin-bottom: 6px;
}

.game-filter-games {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--line-soft);
}

.game-filter-game {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line-soft);
  cursor: pointer;
}

.game-filter-game input {
  flex: none;
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--accent);
}

.game-filter-actions {
  display: flex;
  gap: 8px;
  padding: 14px 24px;
  border-top: 1px solid var(--line);
}

/* --- The stat bar under the video (live scoring) ---
   Part of the player (see video_stat_bar_controller.js), dark like its
   control bar. Normally it hangs just below the video, in room .game-video
   makes for it — the video keeps its full 16:9, and the bar's height comes
   out of the video's height budget instead. Fullscreen, where the player
   is the whole screen, the video and its controls move up to make the
   same room. */
.game-video:has(.video-stat-bar) {
  /* The least it's ever given; its actual height, which the controller
     keeps on .game-video as --stat-bar-height, is what everything below
     makes room for, so a taller panel moves things rather than covering
     them. Sizing the video itself goes by the least, so it doesn't jump
     around as the bar changes. */
  --stat-bar-min: 60px;
  --stat-bar-height: var(--stat-bar-min);
  box-sizing: content-box;
  padding-bottom: var(--stat-bar-height);
}

.game-video:has(.video-stat-bar) .video-js:not(.vjs-fullscreen) {
  height: auto;
  bottom: var(--stat-bar-height);
}

.video-workspace-active .video-workspace > .game-video:has(.video-stat-bar) {
  width: min(70%, calc(((100vh - 32px) * var(--video-height-fraction) - var(--stat-bar-min)) * 16 / 9));
}

@media (max-width: 1359px) {
  .video-workspace-active .video-workspace > .game-video:has(.video-stat-bar) {
    width: min(100%, calc(((100vh - 32px) * var(--video-height-fraction) - var(--stat-bar-min)) * 16 / 9));
  }
}

@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  .tracking-workspace .video-workspace > .game-video:has(.video-stat-bar) {
    width: min(100vw - var(--panel-width) - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px), (100dvh - var(--stat-bar-min)) * 16 / 9);
  }
}

.vjs-fullscreen:has(> .video-stat-bar) :is(.vjs-tech, .vjs-poster, .vjs-tap-layer, .vjs-pan-layer, .vjs-modal-dialog) {
  top: 0;
  bottom: auto;
  height: calc(100% - var(--stat-bar-height));
}

.vjs-fullscreen:has(> .video-stat-bar) .vjs-control-bar {
  bottom: var(--stat-bar-height);
}

/* Until it's moved into the player, a moment after the frame renders. */
.game-stats .video-stat-bar {
  display: none;
}

.video-stat-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(-1 * var(--stat-bar-height));
  z-index: 5;
  display: flex;
  min-height: var(--stat-bar-min);
  background: #0f110f;
  /* A shadow rather than a border, which would add to the height. */
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.08);
  color: #fff;
  font-size: 16px;
  line-height: 1.3;
  container: stat-bar / inline-size;
}

.vjs-fullscreen > .video-stat-bar {
  bottom: 0;
}

.video-stat-bar-main {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 12px;
  padding: 6px 12px;
}

.video-stat-bar:has(> dialog[open]) > .video-stat-bar-main {
  display: none;
}

/* video.js strips every button inside the player of its background,
   border, color and font size (.video-js button), which outranks the
   site's single-class button styles — so the bar's buttons get them back
   here, the same as everywhere else. */
.video-stat-bar .btn {
  border: 1px solid transparent;
  background: var(--accent);
  color: #fff;
  font-size: 0.95rem;
}

.video-stat-bar .btn-shot {
  background: #2563eb;
  border-color: #2563eb;
}

.video-stat-bar .btn-turnover {
  background: #d9730d;
  border-color: #d9730d;
}

.video-stat-bar .btn-point {
  background: var(--win);
}

.video-stat-bar .btn-against {
  background: var(--loss);
}

.video-stat-bar .btn-timeout {
  background: #b8860b;
}

.video-stat-bar .btn-secondary {
  background: var(--surface);
  border-color: var(--line-strong);
  color: var(--ink);
}

.video-stat-bar.possession-defense .btn-shot,
.video-stat-bar.possession-defense .btn-turnover {
  background: var(--fill);
  border-width: 2px;
}

.video-stat-bar.possession-defense .btn-shot {
  color: #2563eb;
}

.video-stat-bar.possession-defense .btn-turnover {
  color: #d9730d;
}

.video-stat-bar .btn-video-nudge {
  border: 1px solid var(--line);
  background: var(--ground-alt);
  color: var(--muted);
  font-size: 0.72rem;
}

/* Left: the score, each team's name under it like the side panel's
   scoreboard, and each team a button to change possession. */
.video-stat-bar-score {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

.video-stat-bar .video-stat-bar-team {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 0;
  min-height: 44px;
  padding: 2px 8px;
  border-radius: var(--radius);
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.68rem;
}

.video-stat-bar .video-stat-bar-team:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
}

.video-stat-bar-name {
  max-width: 12ch;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  line-height: 1.1;
}

.video-stat-bar-points {
  display: flex;
  align-items: center;
  gap: 5px;
  color: #fff;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.video-stat-bar-dash {
  align-self: flex-start;
  margin-top: 8px;
  color: rgba(255, 255, 255, 0.4);
  font-size: 0.9rem;
}

/* Beside whichever team has the ball, like the phone strip's. */
.video-stat-bar-ball {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: #d38b00;
}

.video-stat-bar:not(.possession-defense) .video-stat-bar-team-against .video-stat-bar-ball,
.video-stat-bar.possession-defense .video-stat-bar-team-for .video-stat-bar-ball {
  visibility: hidden;
}

.video-stat-bar-side {
  flex: none;
  margin-left: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--accent-bright);
  color: var(--accent-on-bright);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.video-stat-bar.possession-defense .video-stat-bar-side {
  background: none;
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.65);
  color: #fff;
}

/* Center: the two buttons that matter, bigger than anything else here. */
.video-stat-bar-plays {
  display: flex;
  gap: 10px;
}

.video-stat-bar-plays .btn {
  min-width: 120px;
  min-height: 44px;
  font-size: 1.05rem;
  font-weight: 700;
}

/* Right: quieter, as text buttons. */
.video-stat-bar-extras {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 4px 14px;
}

.video-stat-bar .video-stat-bar-link {
  padding: 6px 2px;
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.8rem;
  white-space: nowrap;
}

.video-stat-bar .video-stat-bar-link:hover {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Each panel — a confirm, the timeout, the wizard — takes the whole bar. */
.video-stat-bar-panel {
  position: static;
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  color: inherit;
}

.video-stat-bar-confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 10px;
  min-height: var(--stat-bar-min);
  padding: 8px 12px;
}

/* The substitution's steps (see _video_stat_bar.html.erb). */
.video-stat-bar-confirm[hidden] {
  display: none;
}

.video-stat-bar-confirm p {
  margin: 0 6px 0 0;
  font-size: 0.95rem;
}

.video-stat-bar-confirm form {
  display: contents;
}

.video-stat-bar-time {
  display: inline-flex;
  align-items: center;
  gap: 2px;
}

.video-stat-bar-time input {
  width: 3.4em;
  padding: 6px;
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.video-stat-bar-to {
  color: rgba(255, 255, 255, 0.6);
  font-size: 0.85rem;
}

/* The wizard, in one row: back and who on the left, the step's choices in
   the middle, the video nudges and close on the right. */
.video-stat-bar-wizard .wizard-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  min-height: var(--stat-bar-min);
  max-height: none;
  padding: 8px 12px;
  border-radius: 0;
  background: none;
  overflow: visible;
}

.video-stat-bar-wizard .wizard-header {
  display: contents;
}

.video-stat-bar-wizard .wizard-back,
.video-stat-bar-wizard .wizard-close {
  min-height: 36px;
  color: rgba(255, 255, 255, 0.72);
}

.video-stat-bar-wizard .wizard-back:hover,
.video-stat-bar-wizard .wizard-close:hover {
  color: #fff;
}

.video-stat-bar-wizard .wizard-back {
  font-size: 0.85rem;
}

.video-stat-bar-wizard .wizard-close {
  order: 3;
  min-width: 36px;
  font-size: 1.4rem;
}

.video-stat-bar-wizard .wizard-heading {
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  max-width: 22ch;
}

.video-stat-bar-wizard .wizard-heading strong {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: 100%;
}

.video-stat-bar-wizard .wizard-side-label {
  color: rgba(255, 255, 255, 0.6);
}

.video-stat-bar-wizard .wizard-step:not([hidden]) {
  order: 1;
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 12px;
}

.video-stat-bar-wizard .wizard-step p {
  margin: 0;
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.video-stat-bar-wizard .wizard-step .meta-info {
  text-transform: none;
  letter-spacing: 0;
}

.video-stat-bar-wizard .wizard-options .btn {
  min-height: 40px;
  font-weight: 600;
}

/* The side panel's phone styles make its checkbox and nudges thumb-sized
   (see .tracking-workspace); the bar keeps them compact. */
.video-stat-bar-wizard .wizard-checkbox {
  min-height: 0;
  margin: 0;
  color: #fff;
}

.video-stat-bar-wizard .wizard-video-controls {
  order: 2;
  margin: 0;
}

.video-stat-bar-wizard .wizard-video-controls .btn-video-nudge {
  flex: none;
  min-height: 30px;
  font-size: 0.72rem;
  font-weight: 400;
}

/* Narrower: shorter team names, and the quieter buttons stacked. */
@container stat-bar (max-width: 860px) {
  .video-stat-bar-name {
    max-width: 8ch;
  }

  .video-stat-bar-plays .btn {
    min-width: 96px;
  }

  .video-stat-bar-extras {
    flex-direction: column;
    align-items: flex-end;
  }

  .video-stat-bar .video-stat-bar-link {
    padding: 2px;
  }
}

/* Phone-wide (portrait): Shot and Turnover stay side by side in the
   middle, and what's beside them stacks instead — Offense/Defense above
   the score, Timeout above Undo — with the wizard's choices on a row of
   their own under its heading. Portrait has height to spare, so the bar
   is taller here (see the phone media query below) rather than cramped,
   and a step with more choices than fit on a row takes another. */
@container stat-bar (max-width: 600px) {
  .video-stat-bar-main {
    gap: 8px;
    padding: 6px 8px;
  }

  .video-stat-bar-score {
    display: grid;
    grid-template-columns: auto auto auto;
    justify-content: start;
    justify-items: center;
    row-gap: 4px;
  }

  .video-stat-bar-side {
    grid-row: 1;
    grid-column: 1 / -1;
    margin: 0;
    padding: 2px 7px;
    font-size: 0.6rem;
  }

  .video-stat-bar-name {
    max-width: 6ch;
  }

  .video-stat-bar .video-stat-bar-team {
    min-height: 0;
    padding: 2px 4px;
  }

  .video-stat-bar-dash {
    margin-top: 4px;
  }

  .video-stat-bar-plays {
    gap: 6px;
  }

  .video-stat-bar-plays .btn {
    min-width: 0;
    padding: 8px 10px;
    font-size: 0.95rem;
  }

  .video-stat-bar .video-stat-bar-link {
    white-space: normal;
    text-align: right;
  }

  .video-stat-bar-wizard .wizard-card {
    gap: 6px 10px;
    padding: 6px 8px;
  }

  .video-stat-bar-wizard .wizard-heading {
    flex: 1;
    min-width: 0;
  }

  .video-stat-bar-wizard .wizard-back,
  .video-stat-bar-wizard .wizard-close {
    min-height: 30px;
  }

  .video-stat-bar-wizard .btn-video-nudge {
    white-space: nowrap;
  }

  .video-stat-bar-wizard .wizard-step:not([hidden]) {
    order: 4;
    flex-basis: 100%;
    gap: 6px 8px;
  }

  .video-stat-bar-wizard .wizard-options {
    justify-content: center;
    gap: 6px;
  }

  .video-stat-bar-wizard .wizard-options .btn {
    min-height: 38px;
    padding: 6px 12px;
  }
}

@media (max-width: 600px) {
  .game-video:has(.video-stat-bar) {
    --stat-bar-min: 96px;
  }
}

/* Temporary, while a few scorers try both ways (see
   scoring_layout_controller.js): scoring from the bar under the video, the
   side panel keeps just what the bar doesn't do — finishing (or starting)
   the game, substitutions, timeouts so far and the stats — so the stats
   move up. Phones keep the ⋯ menu for Finish Game. */
.scoring-under-video .game-actions-card :is(.button-row, .voice-status, .undo-row),
.scoring-under-video .video-controls-card,
.scoring-under-video .player-action-group,
.scoring-under-video .player-action-label,
.scoring-under-video .scoreboard-card,
.scoring-under-video .tracking-score,
.scoring-under-video .tracking-voice-button,
.scoring-under-video .tracking-voice-status,
.scoring-under-video .tracking-undo {
  display: none;
}

.scoring-under-video .tracking-menu {
  margin-left: auto;
}
