/* App layer on top of the Sideout design system (styles.css): topnav, league
   subnav, notifications, dev widgets, draft room, bracket, table extras. */

:root {
  /* Topnav sticky offset; zeroed on mobile where the topnav goes static.
     64px covers the topnav's real rendered height (min-height 60px). */
  --sticky-top: 64px;
}

/* ── Bare-element fallbacks ── */
button {
  font-family: var(--font-text);
  font-size: 14px;
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  padding: 10px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--text-on-accent);
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
button:hover { background: var(--accent-hover); }
button:active { transform: translateY(0.5px); }
button[disabled] {
  background: var(--disabled-bg);
  color: var(--disabled-text);
  cursor: not-allowed;
  pointer-events: none;
}

input:not([type="checkbox"]):not([type="radio"]), select, textarea {
  font-family: var(--font-text);
  font-size: var(--size-body);
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
input:not([type="checkbox"]):not([type="radio"]):focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus-ring);
}
input::placeholder, textarea::placeholder { color: var(--text-subtle); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: 16px; height: 16px; }

/* Text-styled button (logout, inline table actions embedded in copy). */
button.linklike {
  background: none;
  border: none;
  color: var(--accent-text);
  padding: 0;
  font-size: inherit;
  font-weight: var(--weight-medium);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
button.linklike:hover { background: none; color: var(--accent); text-decoration-thickness: 2px; }

/* ── Topnav ──────────────────────────────────────────────────────────────── */
.topnav {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  min-height: var(--topbar-h);
  padding: 0 var(--space-8);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
}
.brand {
  font-family: var(--font-display);
  font-weight: var(--weight-heavy);
  text-transform: uppercase;
  font-size: 26px;
  line-height: 1;
  color: var(--text-primary);
  text-decoration: none;
}
.brand:hover { color: var(--text-primary); text-decoration: none; }
.nav-links { display: flex; gap: var(--space-4); align-items: center; }
.nav-links a {
  padding: 7px var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--size-body);
  font-weight: var(--weight-medium);
  color: var(--text-muted);
  text-decoration: none;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.nav-links a:hover { background: var(--bg-sunken); color: var(--text-primary); text-decoration: none; }
.nav-user { color: var(--text-muted); font-size: var(--size-small); }
/* The badge is a link to /profile: `.nav-links a` outranks `.nav-user`, so
   restate the badge sizing here and take the nav's link treatment. */
a.nav-user { font-size: var(--size-small); color: var(--text-muted); text-decoration: none; }
a.nav-user:hover { color: var(--text-primary); text-decoration: none; }

/* Identity form's disabled-state fieldset must not add its own box —
   lay out exactly like the parent form so the button stretches with it. */
#profile-identity fieldset {
  border: 0;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

/* Dark-mode switch in the topnav (persisted via inline script in base.html). */
.theme-toggle { font-size: var(--size-small); color: var(--text-muted); }

.container { max-width: 1080px; margin: 0 auto; padding: var(--space-8) var(--space-8) var(--space-10); }

/* ── League sub-navigation ───────────────────────────────────────────────── */
.league-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  margin: var(--space-6) 0 var(--space-7);
  border-bottom: 1px solid var(--border);
}
.league-nav a {
  font-size: var(--size-body);
  font-weight: var(--weight-semibold);
  color: var(--text-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  padding: var(--space-4) var(--space-5);
  margin-bottom: -1px;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.league-nav a:hover { color: var(--text-primary); text-decoration: none; }
.league-nav a.active { color: var(--text-primary); border-bottom-color: var(--accent); }
/* Pre-draft the draft room is the main event; live it reads "happening now". */
.league-nav a.draft-primary { color: var(--accent-soft-text); }
.league-nav a.draft-live {
  color: var(--warn-soft-text);
  background: var(--warn-soft-bg);
  border-radius: var(--radius-chip) var(--radius-chip) 0 0;
}
.league-nav a.draft-live:hover { text-decoration: none; }

/* ── Topnav league switcher (pure <details>, no JS) ─────────────────────── */
.league-switcher-slot:empty { display: none; }
.league-switcher-slot { display: flex; gap: var(--space-5); align-items: center; }
.league-switcher-slot .topnav-home {
  color: var(--text-muted);
  font-weight: var(--weight-medium);
  font-size: var(--size-body);
  text-decoration: none;
}
.league-switcher-slot .topnav-home:hover { color: var(--text-primary); }
.league-switcher { position: relative; }
.league-switcher > summary {
  cursor: pointer;
  color: var(--text-muted);
  font-weight: var(--weight-medium);
  font-size: var(--size-body);
  list-style: none;
}
.league-switcher > summary:hover { color: var(--text-primary); }
.league-switcher > summary::after { content: " ▾"; font-size: 0.8em; }
.league-switcher-list {
  position: absolute;
  left: 0;
  top: calc(100% + 8px);
  z-index: 30;
  margin: 0;
  padding: var(--space-2) 0;
  list-style: none;
  min-width: 240px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.league-switcher-list li { padding: 0; margin: 0; }
.league-switcher-list a {
  display: block;
  padding: var(--space-3) var(--space-5);
  color: var(--text-primary);
  text-decoration: none;
  white-space: nowrap;
}
.league-switcher-list a:hover { background: var(--bg-sunken); }
.league-switcher-list li.muted { padding: var(--space-3) var(--space-5); }

/* ── Flash / status messages ─────────────────────────────────────────────── */
.flash-slot:empty { display: none; }
.muted { color: var(--text-muted); font-size: var(--size-small); }
.stats-note { color: var(--text-muted); font-size: var(--size-small); }

.error-message, .lineup-error {
  color: var(--danger-soft-text);
  background: var(--danger-soft-bg);
  border: 1px solid var(--danger);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}
.warning-message {
  color: var(--warn-soft-text);
  background: var(--warn-soft-bg);
  border: 1px solid var(--warn-soft-text);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}
.flash-ok, .success-message {
  display: block;
  color: var(--success-soft-text);
  background: var(--success-soft-bg);
  border: 1px solid var(--success);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
}
.lineup-week-note {
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
  /* A narrow centered callout, not a bar flush with the card edges below. */
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}
.lineup-week-note.locked {
  border-left-color: var(--danger);
  background: var(--danger-soft-bg);
}
/* Season wrap-up banner (league complete): celebratory, not a warning. */
.lineup-week-note.season-result {
  border-left-color: var(--accent);
  background: var(--accent-soft-bg);
}

/* ── Forms baseline (settings, trade, league create, filters) ────────────── */
.auth-form { display: flex; flex-direction: column; gap: var(--space-5); max-width: 384px; margin: 0 auto; }
.auth-title { text-align: center; }
/* Draft-prep guidance card: compact explainer above the queue/pool panes. */
.guidance-card { margin-bottom: var(--space-6); }
.guidance-card h2 { font-size: var(--size-title-sm); margin-bottom: var(--space-3); }
.guidance-card ul { margin-bottom: 0; color: var(--text-muted); }
.guidance-card > summary {
  cursor: pointer;
  font-size: var(--size-title-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  list-style: none;
}
.guidance-card > summary::after { content: " ▾"; font-size: 0.8em; }
.guidance-card[open] > summary { margin-bottom: var(--space-3); }
.guidance-card[open] > summary::after { content: " ▴"; }
.guidance-card > summary::-webkit-details-marker { display: none; }
.guidance-card > ul { margin-bottom: 0; color: var(--text-muted); }
form:not(.auth-form) {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: 512px;
}
form label {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--size-small);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
form .check-row { display: flex; flex-direction: row; align-items: center; gap: var(--space-3); }
form .check-row label { display: inline-flex; flex-direction: row; align-items: center; gap: var(--space-2); margin: 0; font-weight: var(--weight-regular); color: var(--text-body); }

form.filters {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  max-width: none;
}
form.filters input[type="search"] { flex: 0 1 220px; min-width: 160px; }
.position-checks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-5);
  border: none;
  margin: 0;
  padding: 0;
}
form label.pos-check {
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-regular);
  color: var(--text-body);
}

/* Free-agent action column: button+dropdown pair centers under "Action".
   Overrides td form's wrap default so button+select stay on one row. */
.free-agent-table .fa-action { text-align: center; }
.free-agent-table .fa-action form {
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  max-width: none;
}
.free-agent-table .fa-action select {
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Small action forms inside table cells stay compact and inline. */
td form, th form {
  display: inline-flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  max-width: none;
}
/* Rename form holds one line: re-assert row direction (the global
   form:not(.auth-form) column rule outranks plain `td form`) and disable wrap;
   the .table-scroll wrapper absorbs the extra table width. */
td form.rename-form { flex-direction: row; flex-wrap: nowrap; }
td form.rename-form input[name="display_name"] { width: 11em; }
td form.rename-form input[name="username"] { width: 8em; }
/* Placeholder spans in table cells without buttons match form layout. */
.table-action-cell {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-2);
}
/* Admin school registry's inline row editor: a full-width form living in one
   colspan cell, not a compact `td form` action control — it opts back out of
   that rule's inline row and lays its fields out as a responsive grid. */
td form.school-edit-form {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  gap: var(--space-4);
  width: 100%;
}
.school-edit-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}
.school-edit-fields input, .school-edit-fields select { width: 100%; }
/* form.inline-form: the base form:not(.auth-form) rule outranks a lone
   class selector and renders each cell's form as a block flex column with
   gap/max-width, stacking Queue/Draft and varying row heights. */
form.inline-form { display: inline-flex; flex-direction: row; gap: 0; margin: 0; max-width: none; }

form.settings-form { max-width: none; }
.season-tabs a {
  color: var(--text-muted);
  text-decoration: none;
  font-weight: var(--weight-semibold);
  font-size: var(--size-small);
  padding: var(--space-1) var(--space-4);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.season-tabs a:hover { color: var(--text-primary); border-color: var(--text-subtle); }
/* Team-page stats-season switch: same chip look, plus a selected state. */
.season-tabs { display: flex; gap: var(--space-3); }
.season-tabs a.active {
  color: var(--text-primary);
  border-color: var(--accent);
}
.settings-form fieldset {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-7) var(--space-7);
  margin: 0 0 var(--space-6);
  max-width: none;
  scroll-margin-top: calc(var(--sticky-top) + 53px);
  /* Cards read as white surfaces, not the tan page background they'd
     otherwise inherit from .container/.pane. */
  background: var(--bg-surface);
}
.settings-form legend {
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  padding: 0 var(--space-2);
}
.field-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(192px, 1fr));
  gap: var(--space-5) var(--space-7);
}

/* ── Notification bell + panel ───────────────────────────────────────────── */
.notif-bell-wrap { position: relative; }
.notif-bell-btn {
  position: relative;
  background: none;
  border: none;
  color: var(--text-muted);
  line-height: 1;
  padding: var(--space-3);
  cursor: pointer;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.notif-bell-btn:hover { background: var(--bg-sunken); color: var(--text-primary); }
.notif-bell-btn svg { width: 18px; height: 18px; }
.notif-badge-slot:empty { display: none; }
.notif-badge {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--danger);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: var(--weight-bold);
  line-height: 17px;
  text-align: center;
}
.notif-panel {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 320px;
  max-width: calc(100vw - 32px);
  max-height: 70vh;
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  z-index: 50;
  padding: var(--space-3);
}
.notif-panel.open { display: block; }
.notif-list { list-style: none; margin: 0; padding: 0; }
.notif-item {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-1);
  border-bottom: 1px solid var(--border);
}
.notif-item:last-child { border-bottom: none; }
.notif-item.is-read { opacity: 0.6; }
.notif-item-body p { margin: 0 0 2px; }
.notif-title { font-weight: var(--weight-semibold); color: var(--text-primary); }
.notif-detail { font-size: var(--size-small); color: var(--text-muted); }
.notif-time { font-family: var(--font-mono); font-size: var(--size-micro); color: var(--text-subtle); }
.notif-mark-read {
  flex-shrink: 0;
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 4px var(--space-3);
  font-size: var(--size-micro);
  font-weight: var(--weight-semibold);
}
.notif-mark-read:hover { background: var(--bg-surface); border-color: var(--text-subtle); }
.notif-empty { padding: var(--space-3) var(--space-1); color: var(--text-muted); }
.notif-list-actions { display: flex; justify-content: flex-end; padding: var(--space-2) var(--space-1); }

/* ── Dev-only widgets (demo mode; absent in production) ─────────────────── */
.dev-impersonate-slot:empty { display: none; }
.dev-impersonate { position: relative; }
.demo-controls-slot:empty { display: none; }
.demo-controls { position: relative; }
.dev-impersonate > summary,
.demo-controls > summary {
  cursor: pointer;
  list-style: none;
  color: var(--text-muted);
  font-size: var(--size-small);
  padding: var(--space-2) var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
}
.dev-impersonate-list,
.demo-controls-body {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  max-width: calc(100vw - 32px);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  z-index: 50;
  padding: var(--space-3);
}
.dev-impersonate-list { width: 288px; list-style: none; margin: 0; }
.dev-impersonate-list li { padding: 2px 0; }
.dev-impersonate-current {
  font-weight: var(--weight-semibold);
  font-size: var(--size-small);
  color: var(--text-primary);
  padding: var(--space-1);
  display: inline-block;
}
.demo-controls-body { width: 360px; max-width: 90vw; }
.demo-controls-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-3); }
.demo-controls-leagues { list-style: none; margin: 0; padding: 0; font-size: var(--size-small); }
.demo-controls-leagues li { padding: 2px 0; }

/* ── Draft room ── */
@keyframes draft-countdown { from { width: 100%; } to { width: 0%; } }
.countdown-track {
  background: var(--bg-sunken);
  height: 6px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  margin: 0.5em 0;
}
.countdown-bar {
  height: 100%;
  background: var(--accent);
  animation-name: draft-countdown;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}
.on-the-clock.your-turn { color: var(--success-soft-text); font-weight: var(--weight-bold); }
.auto-badge {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--tracking-chip);
  text-transform: uppercase;
  color: var(--text-subtle);
}
.draft-status-pin {
  position: sticky;
  top: var(--sticky-top);
  z-index: 5;
  background: var(--bg-page);
  border-bottom: 1px solid var(--border);
}
.draft-status { padding: var(--space-4) 0 var(--space-3); }
.draft-status__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
}
.draft-status__meta { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.draft-status__team { display: flex; flex-direction: column; gap: 2px; }

/* Viewer's own turn: status bar fills with pulsing accent wash in place,
   so the pin's height never jumps between your-turn and quiet renders. */
.draft-status--your-turn {
  background: var(--accent-soft-bg);
  animation: draft-your-turn-pulse 1.8s ease-in-out infinite;
}
@keyframes draft-your-turn-pulse {
  0%, 100% { box-shadow: inset 0 0 0 0 var(--accent-soft-bg); }
  50% { box-shadow: inset 0 0 24px 4px var(--accent-soft-bg); }
}
@media (prefers-reduced-motion: reduce) {
  .draft-status--your-turn { animation: none; }
}

/* The room's sections stack with a consistent gap; card tables keep their
   headers in-card (the page-level sticky offset would float them under the
   pinned status bar). */
#available-players { display: flex; flex-direction: column; gap: var(--space-6); }
#available-players .stat-table thead th { position: static; }
/* form.draft-pending__start: the base form:not(.auth-form) rule outranks a
   lone class selector and leaves the button's block box flush left inside
   the centered .empty card. */
form.draft-pending__start { display: flex; flex-direction: column; align-items: center; margin-top: var(--space-5); margin-inline: auto; }
/* Pool row actions: Queue sits beside Draft, both compact. Stays a table
   cell (no display override) so the row's border-bottom renders correctly. */
.pool-actions { text-align: right; white-space: nowrap; }
.pool-actions form + form { margin-left: var(--space-3); }

/* Top row: the status card with the league-wide pick feed beside it. Outside
   the lock they stack and only the status pins; the feed's list scrolls
   internally so the card stays shallow. */
.draft-top-row { display: flex; flex-direction: column; gap: var(--space-5); }
.draft-top-row .card { margin: 0; }
#draft-recent-picks .pick-feed { max-height: 180px; overflow-y: auto; }
/* Kept compact even outside the desktop lock, where the top row still
   stacks — the feed's own list is what scrolls, not the card. */
#draft-recent-picks { max-height: 260px; }

/* Below the viewport lock the room's regions simply stack; the lock turns
   this into the kit's pool / my-picks / rail / board grid (see the media
   block at the end). */
.draft-room-grid {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  margin-top: var(--space-6);
}

/* ── Rail ── */
.draft-rail { display: flex; flex-direction: column; gap: var(--space-5); }
.draft-rail .card { margin: 0; }
/* Filled roster-slot row: `.muted` also shrinks font-size, which visibly
   demotes filled rows against open ones in this tight slot list — color
   distinction only, same size as every other row. */
.roster-slot--filled td { color: var(--text-muted); }

.draft-queue { display: flex; flex-direction: column; gap: var(--space-3); }
.draft-queue__row {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  align-items: start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--bg-sunken);
  border-radius: var(--radius-md);
}
.draft-queue__rank { font-size: var(--size-micro); color: var(--text-subtle); line-height: 1.6; }
.draft-queue__id { min-width: 0; }
/* Actions wrap under the name rather than squeezing it — the rail is narrow. */
.draft-queue__actions {
  grid-column: 2;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.draft-queue__row.ranking-dead { opacity: 0.55; }
.draft-queue__empty { margin: 0; font-size: var(--size-small); }

/* Compact pick feed — "My picks" and "Recent picks", per the kit. */
.pick-feed { display: flex; flex-direction: column; gap: var(--space-4); }
.pick-feed__row { display: flex; align-items: flex-start; gap: var(--space-3); }
.pick-feed__num {
  flex: none;
  width: 34px;
  font-size: var(--size-micro);
  color: var(--text-subtle);
  line-height: 1.6;
}
.pick-feed__id { min-width: 0; }
.pick-feed .muted { margin: 0; font-size: var(--size-small); }

/* ── Draft board strip ── */
.team-boards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(196px, 1fr));
  gap: var(--space-4);
  align-content: start;
  /* No height cap: the board holds every card and the page scrolls. Under
     the viewport lock the mid-draft board carries `pane` and scrolls in its
     grid row; a complete draft drops `pane` and scrolls the page. */
}
.team-board-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  background: var(--bg-surface);
}
.team-board-col__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-3);
}
.team-board-col__name {
  font-size: var(--size-small);
  font-weight: var(--weight-bold);
  color: var(--text-primary);
}
.team-board-pick { display: flex; flex-direction: column; gap: 1px; }
.team-board-pick__num { font-size: var(--size-micro); color: var(--text-subtle); }
.team-board-pick__name { font-size: var(--size-small); line-height: 1.25; }
.team-board-pick__pending {
  font-size: var(--size-small);
  font-weight: var(--weight-bold);
  color: var(--accent-text);
}
.team-board-pick__empty { font-size: var(--size-micro); }
.team-board-col.on-the-clock {
  background: var(--accent-soft-bg);
  border-color: var(--accent);
}
/* Top-row left cell: status card stacked over the completed-draft
   Highlights card. */
.draft-top-col {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  min-width: 0;
}

/* ── Tables: app extras on top of .stat-table ────────────────────────────── */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll.game-log-scroll { max-height: 512px; overflow-y: auto; }
/* Ranked draft queue: reorder swaps happen inside this capped box so the
   page scroll position survives each full-list re-render. */
.table-scroll.rankings-scroll { max-height: 448px; overflow-y: auto; }

.pagination { display: flex; align-items: center; gap: var(--space-6); margin: var(--space-5) 0; }

.stat-table thead th {
  position: sticky;
  top: var(--sticky-top);
  z-index: 2;
  background: var(--bg-surface);
}
/* Inside a scroll wrapper the header sticks to the wrapper, not the page. */
.table-scroll .stat-table thead th { top: 0; }
.stat-table.stat-nums td, .stat-table.stat-nums th {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.stat-table.stat-nums td { font-family: var(--font-mono); }
.stat-table.stat-nums td.txt, .stat-table.stat-nums th.txt { text-align: left; font-family: var(--font-text); }
.stat-table.freeze-first th:first-child,
.stat-table.freeze-first td:first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--bg-surface);
  border-right: 1px solid var(--border);
}
.stat-table.freeze-first thead th:first-child { z-index: 3; }
.stat-table tbody tr:hover td { background: var(--bg-sunken); }
.stat-table.freeze-first tbody tr:hover td:first-child { background: var(--bg-sunken); }
.stat-table--striped tbody tr:nth-child(even) td { background: var(--bg-surface); }
.stat-table--striped tbody tr:hover td { background: var(--bg-sunken); }
/* Admin tables (all use --striped) get a soft violet header band for
   legibility against the plain --bg-surface sticky header. Wins over
   `.stat-table thead th` above on source order (equal specificity). */
.stat-table--striped thead th {
  background: var(--accent-soft-bg);
  color: var(--accent-text);
  font-weight: var(--weight-semibold);
}
/* Freeze-first's flat th/td:first-child background (line above) would hide
   the striped header/row tint on tables using both modifiers. Higher
   specificity wins; hover rule stays last to win the parity tie. */
.stat-table--striped.freeze-first thead th:first-child {
  background: var(--accent-soft-bg);
  color: var(--accent-text);
}
.stat-table--striped.freeze-first tbody tr:nth-child(even) td:first-child { background: var(--bg-surface); }
/* Odd rows are unstyled elsewhere, so they show through to the page
   background (sand tone) — the sticky cell needs that spelled out. */
.stat-table--striped.freeze-first tbody tr:nth-child(odd) td:first-child { background: var(--bg-page); }
.stat-table--striped.freeze-first tbody tr:hover td:first-child { background: var(--bg-sunken); }

/* Column-group header row that labels Attack / Serve / Defense spans. */
.stat-table .col-group th {
  text-align: center;
  border-bottom: 1px solid var(--border);
}
/* Items never shrink below their table's natural width (min-width:max-content),
   so they wrap instead of compressing. 760px cap keeps the block compact on
   wide panes. components.css .stat-table { width:100% } fills each item. */
.stat-split { display: flex; flex-wrap: wrap; gap: var(--space-7); margin-bottom: var(--space-6); max-width: 760px; }
.stat-split > div { flex: 1 1 auto; min-width: max-content; }
.stat-split h3 { margin: 0 0 var(--space-2); font-size: var(--size-body); }

/* ── Native <dialog> modal ── */
dialog.modal {
  background: var(--bg-surface);
  color: var(--text-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: var(--space-7);
  width: min(560px, 92vw);
  max-height: 88vh;
  overflow: auto;
}
dialog.modal--wide { width: min(1100px, 92vw); }
/* Near-full viewport: the identity-review compare table is two nine-column
   game logs side by side plus an action column. */
dialog.modal--full { width: min(1680px, 96vw); max-height: 94vh; }
dialog.modal::backdrop { background: rgba(20, 17, 14, 0.55); }
/* Pinned inside the scrolling dialog: needs opaque background plus a
   z-index above the compare table's sticky header and frozen corner. */
dialog.modal .pane-head { top: calc(-1 * var(--space-7)); margin-top: calc(-1 * var(--space-7)); padding-top: var(--space-7); padding-inline: var(--space-7); margin-inline: calc(-1 * var(--space-7)); padding-bottom: var(--space-4); background: var(--bg-surface); z-index: 4; }

/* A game date both sides of a merge candidate have a line for — exactly one
   survives, so the row carries the choice radio. */
.stat-table tr.is-conflict > td { background: var(--warn-soft-bg); color: var(--warn-soft-text); }

/* ── Identity-review compare modal ── */
/* The generic `form:not(.auth-form)` cap (384px... 512px, above) would trap
   this 19-column table in a sliver of the wide dialog — opt out like
   form.settings-form does. */
form.compare { max-width: none; }

.compare-cards {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.compare-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-sunken);
  padding: var(--space-5);
  min-width: 0;
}
.compare-card h3 { margin: 0 0 var(--space-2); font-size: var(--size-body); }
.compare-card p { margin: 0; }
.compare-gutter { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; }

.field-choices { margin-bottom: var(--space-6); }
.field-choices > p { margin: 0 0 var(--space-3); }
.field-choice {
  display: grid;
  grid-template-columns: 8rem 1fr 1fr;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-2) 0;
}
.field-choice__label { color: var(--text-muted); }
.field-choice label { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

/* Compact density so 19 columns breathe at full modal width — mirrors
   .admin-section--compact .stat-table's tighter cells above. */
.compare-table { width: 100%; font-size: var(--size-small); }
.compare-table th, .compare-table td { padding: var(--space-2) var(--space-3); }

/* Centre divider between the two players' column blocks. */
.compare-table th.b-start, .compare-table td.b-start { border-left: 2px solid var(--border); }
.compare-table td.absent { color: var(--text-muted); font-style: italic; }
.compare-table td.action-cell { white-space: nowrap; }
.compare-table td.action-cell label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-right: var(--space-3);
}

/* Selection feedback, no JS: an abandoned date strikes its whole row, and
   picking one side dims the other side's cells. `:has()` on the row reads the
   radio state live. */
.compare-table tbody tr:has(input[value="drop"]:checked) > td {
  opacity: 0.5;
  text-decoration: line-through;
}
.compare-table tbody tr:has(input[value="drop"]:checked) > td.action-cell { text-decoration: none; }
.compare-table tbody tr.is-conflict:has(input.keep-a:checked) > td.b-start,
.compare-table tbody tr.is-conflict:has(input.keep-a:checked) > td.b-start ~ td:not(.action-cell) { opacity: 0.45; }
.compare-table tbody tr.is-conflict:has(input.keep-b:checked) > td.a-col { opacity: 0.45; }

/* Sortable header button reads as a header, acts as a button. */
button.sort-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-chip);
  text-transform: uppercase;
  color: var(--accent-text);
  cursor: pointer;
}
button.sort-btn:hover { background: none; text-decoration: underline; }

/* ── Admin table tools ── */
.table-tools {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  margin: var(--space-3) 0 var(--space-3) 0;
}
.table-tools__filter { flex: 0 1 240px; min-width: 0; }
.table-tools__count {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  color: var(--text-subtle);
  white-space: nowrap;
}
/* Adopted page-level action (see table-tools.js findAside) — pins right on
   whichever table's Filter row it lands in. */
.table-tools__aside { margin-left: auto; }
/* [hidden] alone loses to any display rule on rows; be explicit. */
.stat-table tbody tr[hidden] { display: none; }
.stat-table th.th--sortable { cursor: pointer; user-select: none; }
.stat-table th.th--sortable:hover { color: var(--text-primary); }
.stat-table th.th--sortable::after { content: "\2195"; margin-left: var(--space-2); opacity: 0.35; }
.stat-table th.th--sortable[aria-sort="ascending"]::after { content: "\2191"; opacity: 1; color: var(--accent-text); }
.stat-table th.th--sortable[aria-sort="descending"]::after { content: "\2193"; opacity: 1; color: var(--accent-text); }
.stat-table[data-table-tools] thead th { font-size: var(--size-small); }

/* ── Slot-kind grouping (lineup table + matchup head-to-head) ── */
tr.slot-row--flex td { background: var(--accent-soft-bg); }
tr.slot-row--flex td:first-child { border-left: 3px solid var(--accent); }
tr.slot-row--bench td { background: var(--bg-sunken); color: var(--text-muted); }
tr.slot-row--bench td:first-child { border-left: 3px solid var(--border); }
tr.slot-row--bench td a:not(.btn) { color: var(--text-muted); }

/* ── Position identity: chips + per-position starter-row tint ── */
.badge--pos-s   { background: var(--pos-s-soft-bg);   color: var(--pos-s-soft-text); }
.badge--pos-oh  { background: var(--pos-oh-soft-bg);  color: var(--pos-oh-soft-text); }
.badge--pos-opp { background: var(--pos-opp-soft-bg); color: var(--pos-opp-soft-text); }
.badge--pos-mb  { background: var(--pos-mb-soft-bg);  color: var(--pos-mb-soft-text); }
.badge--pos-l   { background: var(--pos-l-soft-bg);   color: var(--pos-l-soft-text); }
.badge--pos-ds  { background: var(--pos-l-soft-bg);   color: var(--pos-l-soft-text); }
.badge--pos-rs  { background: var(--pos-opp-soft-bg); color: var(--pos-opp-soft-text); }
/* Combined positions ("S/OPP") render one chip per component; wraps to a
   second line instead of widening the shared table column. */
.pos-badges {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  vertical-align: middle;
  max-width: 100%;
}
/* Larger chip for slot labels in the lineup rail's slot-overview card. */
.badge--lg {
  font-size: var(--size-small);
  padding: var(--space-2) var(--space-5);
}
/* The meta line's chip stays lighter than a standalone badge. */
.player__meta .badge { padding: 1px 6px; }
tr.slot-row--pos-s td:first-child { border-left: 3px solid var(--pos-s-soft-text); }
tr.slot-row--pos-oh td:first-child { border-left: 3px solid var(--pos-oh-soft-text); }
tr.slot-row--pos-opp td:first-child { border-left: 3px solid var(--pos-opp-soft-text); }
tr.slot-row--pos-mb td:first-child { border-left: 3px solid var(--pos-mb-soft-text); }
tr.slot-row--pos-l td:first-child, tr.slot-row--pos-ds td:first-child { border-left: 3px solid var(--pos-l-soft-text); }
tr.slot-row--pos-pin td:first-child { border-left: 3px solid var(--pos-oh-soft-text); }
/* An open seat keeps its slot's edge stripe but mutes the dash cells. */
tr.slot-row--empty td { color: var(--text-muted); }
/* Position row wash is dark-mode only; light mode keeps just the edge stripe. */
[data-theme="dark"] tr.slot-row--pos-s td { background: var(--pos-s-soft-bg); }
[data-theme="dark"] tr.slot-row--pos-oh td { background: var(--pos-oh-soft-bg); }
[data-theme="dark"] tr.slot-row--pos-opp td { background: var(--pos-opp-soft-bg); }
[data-theme="dark"] tr.slot-row--pos-mb td { background: var(--pos-mb-soft-bg); }
[data-theme="dark"] tr.slot-row--pos-l td, [data-theme="dark"] tr.slot-row--pos-ds td { background: var(--pos-l-soft-bg); }
[data-theme="dark"] tr.slot-row--pos-pin td { background: var(--pos-oh-soft-bg); }
.h2h-half .h2h-slot { text-align: center; white-space: nowrap; width: 1%; }
.h2h-half th.h2h-right, .h2h-half td.h2h-right { text-align: right; }
/* Initials chips sit inline with the player links, compact for the dense
   head-to-head rows. */
.h2h-half .player__avatar {
  display: inline-grid;
  vertical-align: middle;
  width: 24px;
  height: 24px;
}

/* ── Dashboard card grid ─────────────────────────────────────────────────── */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(256px, 1fr));
  gap: var(--space-6);
  margin: var(--space-6) 0 var(--space-7);
}
/* League dashboard: explicit rows — This week (2/3) + Your team (1/3),
   then Standings + Trades & waivers, commissioner full-width last. */
.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-6);
  margin: var(--space-6) 0 var(--space-7);
}
.dashboard-grid > .dash-week { grid-column: span 4; }
.dashboard-grid > .dash-team { grid-column: span 2; }
.dashboard-grid > .dash-standings { grid-column: span 3; }
.dashboard-grid > .dash-waivers { grid-column: span 3; }
.dashboard-grid > .dash-comm { grid-column: 1 / -1; }
/* Off-season the "This week" card is omitted; "Your team" leads and takes
   the whole first row so the pairs below keep their clean two-up rows. */
.dashboard-grid > .dash-team:first-child { grid-column: 1 / -1; }
@media (max-width: 900px) {
  .dashboard-grid > * { grid-column: 1 / -1; }
}
.card h2 {
  margin: 0 0 var(--space-4);
  font-family: var(--font-text);
  font-size: var(--size-title-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-title);
}
.card.card-cta {
  border-color: var(--accent);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.card-cta .btn { align-self: flex-start; }
.card-links { list-style: none; margin: 0; padding: 0; }
.card-links li { padding: var(--space-1) 0; margin: 0; }
.card-links a { font-weight: var(--weight-semibold); text-decoration: none; }
.card-links a:hover { text-decoration: underline; }

/* ── Season tab strip (player-detail stats panel) ────────────────────────── */
.season-tabs {
  display: flex;
  gap: var(--space-2);
  border-bottom: 1px solid var(--border);
  margin: var(--space-6) 0 var(--space-5);
}
.season-tabs .tab { margin-bottom: -1px; }

.admin-title { margin-bottom: var(--space-9); }

.admin-section {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-7);
  margin: var(--space-7) 0;
}
.admin-section h2 {
  margin-top: 0;
  font-family: var(--font-text);
  font-size: var(--size-title-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-title);
}

.admin-section--compact { padding: var(--space-5) var(--space-6); }
.admin-section--compact .stat-table th,
.admin-section--compact .stat-table td { padding: var(--space-2) var(--space-3); font-size: var(--size-small); }

.tabs.pane-head:not(:first-child) { margin-top: var(--space-8); padding-top: 0; }
.tabs.pane-head + * { margin-top: var(--space-6); }

.sync-audit-bar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-6);
}
.sync-audit-bar > form { max-width: none; }
.sync-audit-filters__row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 200px));
  gap: var(--space-5);
}

/* Stats-audit editor: fixed-width number inputs center their abbreviation
   over the box instead of the default flush-right .num alignment. */
.stats-audit-table thead th.num,
.stats-audit-table td.num { text-align: center; }
.stats-audit-table td.num input[type="number"] { width: 7ch; text-align: center; }
.stats-audit-table thead th.num.th--sortable { position: relative; }
.stats-audit-table thead th.num.th--sortable::after {
  position: absolute;
  right: var(--space-3);
  top: 50%;
  margin-left: 0;
  transform: translateY(-50%);
}

/* ── Trade cards ─────────────────────────────────────────────────────────── */
.trade-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.trade-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  padding: var(--space-5) var(--space-6);
}
.trade-card > p:first-child { margin-top: 0; }
.trade-card > :last-child { margin-bottom: 0; }

/* ── Trade proposal builder ── */
form.trade-form { max-width: none; }
body:has(form.trade-form) .container { max-width: none; }

.trade-form .trade-pick {
  --pick-accent: var(--border);
  --pick-tint: var(--bg-surface);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--pick-tint);
}
.trade-form .trade-pick--own { border-left: 3px solid var(--pick-accent); }
.trade-form .trade-pick--partner { border-right: 3px solid var(--pick-accent); }
.trade-form .trade-pick--pos-s   { --pick-accent: var(--pos-s-soft-text);   --pick-tint: var(--pos-s-soft-bg); }
.trade-form .trade-pick--pos-oh  { --pick-accent: var(--pos-oh-soft-text);  --pick-tint: var(--pos-oh-soft-bg); }
.trade-form .trade-pick--pos-opp,
.trade-form .trade-pick--pos-rs  { --pick-accent: var(--pos-opp-soft-text); --pick-tint: var(--pos-opp-soft-bg); }
.trade-form .trade-pick--pos-mb  { --pick-accent: var(--pos-mb-soft-text);  --pick-tint: var(--pos-mb-soft-bg); }
.trade-form .trade-pick--pos-l,
.trade-form .trade-pick--pos-ds  { --pick-accent: var(--pos-l-soft-text);   --pick-tint: var(--pos-l-soft-bg); }
/* Beats `form .check-row label` (0,1,1): the label owns the row's columns so
   the stat line and points align down the column instead of ragging. */
.trade-form .trade-pick label {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: auto auto minmax(4ch, auto) minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin: 0;
}
.trade-form .trade-pick input[type="checkbox"] { margin: 0; }
.trade-pick__name {
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trade-pick__school {
  min-width: 0;
  font-size: var(--size-small);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.trade-pick__stats {
  justify-self: end;
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  color: var(--text-muted);
  white-space: nowrap;
}
.trade-pick__fpts {
  justify-self: end;
  font-size: var(--size-small);
  white-space: nowrap;
}
.trade-pick__link { flex: none; font-size: var(--size-small); }
@media (max-width: 900px) {
  /* Narrow: the stat line wraps under the name instead of squeezing it. */
  .trade-form .trade-pick label { grid-template-columns: auto auto minmax(0, 1fr) auto; }
  .trade-pick__school { grid-column: 3 / -1; }
  .trade-pick__stats { grid-column: 1 / -1; justify-self: start; }
}

/* ── Player hover card (trade pending/history lists) ── */
.ph-wrap { position: relative; display: inline; }
.ph-card {
  position: fixed;
  display: none;
  z-index: 60;
  width: max-content;
  max-width: min(300px, calc(100vw - 32px));
  margin: var(--space-5) 0 0 calc(var(--space-3) * -1);
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  box-shadow: var(--shadow-lg);
  color: var(--text-body);
  font-size: var(--size-small);
  /* The card is decoration over a link; clicks belong to what's underneath. */
  pointer-events: none;
}
@media (hover: hover) {
  .ph-wrap:hover .ph-card { display: block; }
}
.ph-wrap:focus-within .ph-card { display: block; }
.ph-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
.ph-card__id { display: block; min-width: 0; }
.ph-card__name {
  display: block;
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
.ph-card__meta {
  display: block;
  font-size: var(--size-micro);
  color: var(--text-muted);
}
.ph-card__meta .badge { padding: 1px 6px; }
.ph-card__label {
  display: block;
  font-size: var(--size-micro);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
}
.ph-card__line {
  display: block;
  font-family: var(--font-mono);
  color: var(--text-primary);
}
.ph-card__context {
  display: block;
  font-size: var(--size-micro);
  color: var(--text-muted);
}

/* ── Playoff bracket ─────────────────────────────────────────────────────── */
/* Geometry comes from `handlers::bracket_layout` (Rust): one flat px
   coordinate space shared by slot positions and the SVG connector paths.
   Change sizes there, never here — CSS here is paint only. */
.bracket-grid {
  display: flex;
  /* `safe` so an overflowing bracket still scrolls from its left edge. */
  justify-content: safe center;
  overflow-x: auto;
  padding: var(--space-6) 0;
}
/* Sized inline from the computed canvas; the positioning context for both
   the slots and the SVG overlay. */
.bracket-canvas { position: relative; flex: 0 0 auto; }
.bracket-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}
.bracket-line {
  fill: none;
  stroke: var(--border-strong);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Undecided matchup: the path still shows the bracket's shape, muted. */
.bracket-line--pending { stroke: var(--border); stroke-width: 1.5; }
/* Semifinal loser feeds the 3rd-place match. */
.bracket-line--loser { stroke-dasharray: 6 5; }
.bracket-round-head {
  position: absolute;
  margin: 0;
  text-align: center;
}
.bracket-slot { position: absolute; }
.bracket-matchup {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  font-size: var(--size-body);
}
.bracket-matchup--championship {
  border: 2px solid var(--warn-soft-text);
  font-size: calc(var(--size-body) * 1.2);
}
/* Positioned above the card rather than stacked in normal flow: the slot is
   sized to the card exactly (`bracket_layout`'s rect), so a caption in flow
   would shrink it and desync the SVG anchors. */
.bracket-championship-label,
.bracket-consolation-label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + var(--space-2));
  margin: 0;
  font-size: var(--size-micro);
  letter-spacing: var(--tracking-chip);
  text-transform: uppercase;
  font-weight: var(--weight-bold);
}
/* Championship label runs ~20% larger than the base (consolation stays
   at the shared base size defined above). */
.bracket-championship-label { color: var(--warn-soft-text); font-size: calc(var(--size-micro) * 1.2); }
.bracket-consolation-label { color: var(--text-muted); }
/* Both team rows flex equally so the divider lands at the card's vertical
   middle, where `bracket_layout` anchors a connector at h/4 / 3h/4. */
.bracket-team, .bracket-bye {
  flex: 1 1 0;
}
.bracket-team {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-5) var(--space-7);
}
.bracket-team:hover { background: var(--bg-sunken); }
.bracket-team.winner {
  background: var(--success-soft-bg);
  font-weight: var(--weight-bold);
  border-left: 3px solid var(--success);
}
.bracket-team.winner:hover { background: var(--success-soft-bg); filter: brightness(0.97); }
.bracket-team + .bracket-team { border-top: 1px solid var(--border); }
.bracket-team__main {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
/* Name + record stack, left-aligned, filling the space between the seed
   chip and the score. */
.bracket-team__info {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  flex: 1 1 auto;
  min-width: 0;
}
/* Medal (if any) + name on one line; the name truncates rather than wraps,
   since the card height is fixed by `bracket_layout`. */
.bracket-team__nameline {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
.bracket-medal { flex: 0 0 auto; font-size: 1.1em; line-height: 1; }
.bracket-team__name {
  color: var(--text-primary);
  text-decoration: none;
  font-size: calc(var(--size-body) * 1.25);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
a.bracket-team__name:hover { color: var(--text-primary); text-decoration: underline; }
.bracket-team__score {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: calc(var(--size-body-lg) * 1.25);
}
.bracket-seed {
  flex: 0 0 auto;
  color: var(--text-subtle);
  padding: 1px var(--space-2);
  border-radius: var(--radius-chip);
  background: var(--bg-sunken);
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--size-small);
}
.bracket-record {
  color: var(--text-muted);
  font-size: var(--size-micro);
}
.bracket-bye {
  display: flex;
  align-items: center;
  color: var(--text-muted);
  font-style: italic;
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border);
}
.bracket-team.bracket-tbd { color: var(--text-muted); font-style: italic; }
/* Championship card: name + record + label scale ~20% above the base
   (and the consolation card, which stays at base size). */
.bracket-matchup--championship .bracket-team { padding: var(--space-6) var(--space-8); }
.bracket-matchup--championship .bracket-team__name { font-size: calc(var(--size-body) * 1.25 * 1.2); }
.bracket-matchup--championship .bracket-record { font-size: calc(var(--size-micro) * 1.2); }
/* Narrow screens: the canvas is a fixed px drawing — it scrolls sideways
   inside `.bracket-grid` rather than reflowing (reflowing would break the
   1:1 DOM↔SVG coordinate match the connectors depend on). */
@media (max-width: 640px) {
  .bracket-grid { padding: var(--space-4) 0; }
}
.bracket-champion {
  margin: var(--space-7) 0;
  padding: var(--space-6);
  background: var(--success-soft-bg);
  border: 1px solid var(--success);
  color: var(--success-soft-text);
  border-radius: var(--radius-lg);
  text-align: center;
  font-family: var(--font-display);
  font-weight: var(--weight-heavy);
  font-size: var(--size-title-lg);
}

/* ── Matchup scoreboard (dashboard "this week" box + matchup page) ── */
.matchup-card .matchup { padding: calc(var(--space-4) * 1.25) 0 calc(var(--space-2) * 1.25); }
.matchup__side { display: flex; flex-direction: column; gap: var(--space-2); flex: 1; min-width: 0; }
.matchup__side--right { text-align: right; align-items: flex-end; }
.matchup__team {
  display: inline-flex;
  min-width: 0;
  max-width: 100%;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
.matchup__team a { color: var(--text-primary); text-decoration: none; }
.matchup__team a:hover { color: var(--text-primary); text-decoration: underline; }
.matchup__medal { font-size: 0.9em; }
.matchup__team-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.matchup-card .bar { margin-top: var(--space-5); }
.matchup-week-detail .matchup-card { margin-bottom: var(--space-6); }

/* ── Matchups headlines page: week's matchups as clickable boxes ───────── */
.matchup-headlines-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(410px, 100%), 1fr));
  gap: var(--space-5);
  align-content: start;
}
/* Room for the hover halo (2px outline + 2px offset) at the pane's clipped
   top/left edges. */
.matchup-pane { padding: var(--space-2); }
a.matchup-box-link { display: block; color: inherit; text-decoration: none; }
a.matchup-box-link .matchup-card { height: 100%; }
a.matchup-box-link:hover .matchup-card,
a.matchup-box-link:focus-visible .matchup-card { outline: 2px solid var(--accent); outline-offset: 2px; }
.matchup-byes { margin-top: var(--space-5); }

/* Weeks split into This week / Upcoming / Completed tabs; the week-jump
   chips hx-get the tab that holds their week and scroll to its #week-N
   anchor inside the swapped panel. */
.matchup-tabs {
  display: flex;
  gap: var(--space-5);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-6);
}
.matchup-tabs__tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: var(--space-3) var(--space-2);
  font: inherit;
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  cursor: pointer;
}
/* Beat the base `button:hover` background (0-1-1) — tabs never fill. */
.matchup-tabs__tab:hover { background: none; color: var(--text-primary); font-weight: var(--weight-semibold); }
button.tab:hover { background: none; color: var(--text-primary); }
.matchup-tabs__tab--active {
  color: var(--text-primary);
  border-bottom-color: var(--accent);
}
/* Decided matchup: winning side's name (and score, on the headlines grid)
   gets a win chip — shared by the headlines grid and matchup detail page. */
.score-chip--win {
  color: var(--success-soft-text);
  background: var(--success-soft-bg);
  border-radius: var(--radius-chip);
  padding: 1px 8px;
}
.week-jump { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-2); }
.week-jump__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  padding: 2px var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-chip);
  font-size: var(--size-small);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  text-decoration: none;
}
.week-jump__chip:hover { border-color: var(--accent); color: var(--text-primary); text-decoration: underline; }
.week-jump__chip--current { background: var(--accent-soft-bg); border-color: var(--accent); color: var(--text-primary); }
/* Anchors scroll inside .matchup-pane (the app shell keeps body overflow
   hidden), so only a small inset from the pane's top edge is needed. */
.week-section { scroll-margin-top: var(--space-4); }
.week-section + .week-section { margin-top: var(--space-8); }
.week-section__head { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-4); }
.week-section__title { margin: 0; }

/* ── Page header (eyebrow label + display title + right-aligned actions) ── */
.page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-6);
  flex-wrap: wrap;
  margin-bottom: var(--space-7);
}
.page-head h1 { margin: 0; }
.page-head__eyebrow { display: block; margin-bottom: var(--space-2); }
.page-head__actions { display: flex; align-items: center; gap: var(--space-4); }

/* ── Lineup page: main column + eligible-swap rail ───────────────────────── */
.lineup-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 425px;
  gap: var(--space-6);
  align-items: start;
}
.lineup-main { display: flex; flex-direction: column; gap: var(--space-6); min-width: 0; }
.lineup-rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  position: sticky;
  top: calc(var(--sticky-top) + var(--space-4));
}
@media (max-width: 900px) {
  .lineup-grid { grid-template-columns: 1fr; }
  .lineup-rail { position: static; }
}
/* Must match form:not(.auth-form)'s (0,1,1) specificity to actually release
   the 512px baseline cap — a lone class loses and the cards render narrow. */
form.lineup-form { max-width: none; }
/* The lineup cards scroll their own tables; headers stick to the card, not
   the page (the page-level sticky offset would float them mid-card). */
.lineup-form .stat-table thead th { position: static; }
/* Cells hold one line so rows stay level; the player cell may still wrap. */
.lineup-table th, .lineup-table td { white-space: nowrap; }
.lineup-table td:nth-child(2) { white-space: normal; }
/* Row actions stack: Change above Drop, equal widths. */
.lineup-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: stretch;
}
.lineup-main { align-items: center; }
.lineup-main > * { width: 100%; max-width: 900px; }
/* form.week-picker: the base form:not(.auth-form) rule outranks a lone
   class selector and re-stacks the picker into a column, wedging the ‹ ›
   steppers above and below the select. */
form.week-picker {
  flex-direction: row;
  align-items: center;
  flex-wrap: nowrap;
  gap: var(--space-3);
  max-width: none;
}
.week-picker label { flex-direction: row; align-items: center; gap: var(--space-3); white-space: nowrap; }
.week-picker select { min-width: 72px; }
.week-picker .btn {
  min-width: 40px;
  padding: var(--space-2) var(--space-3);
  font-size: var(--size-title-md);
  line-height: 1;
}

/* Rail cards: week summary rows + eligible-player list. */
/* One filled-by occupant per line — the chip ends each entry, so stacking
   beats an ambiguous run-on when a group slot holds several players. */
.slot-occupant { display: block; }
.week-summary { display: flex; flex-direction: column; gap: var(--space-3); }
.week-summary > div { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-4); }
.week-summary .num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--size-title-md); color: var(--text-primary); }
.week-summary .num.fpts { color: var(--accent-text); font-weight: var(--weight-semibold); }
.eligible-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.eligible-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  margin: 0;
  border-bottom: 1px solid var(--border);
}
.eligible-list li:last-child { border-bottom: none; }

/* Player cell name link reads as the design's plain semibold name. */
a.player__name { color: var(--text-primary); text-decoration: none; }
a.player__name:hover { color: var(--text-primary); text-decoration: underline; }
.player__id { min-width: 0; }

/* ── Matchup page: mirrored head-to-head split ───────────────────────────── */
.matchup-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 1px minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.matchup-split__divider { background: var(--border); align-self: stretch; }
/* The split's table headers label each side; keep them with their card
   instead of sticking to the page. */
.matchup-split .stat-table thead th { position: static; }
@media (max-width: 900px) {
  .matchup-split { grid-template-columns: 1fr; }
  .matchup-split__divider { display: none; }
}

/* ── Standings ───────────────────────────────────────────────────────────── */
.standings-rank { color: var(--text-subtle); font-family: var(--font-mono); width: 1%; }
/* Playoff cut line: drawn under the last team above the cut. */
tr.standings-cut td { border-bottom: 2px solid var(--accent); }
.standings-cut-note { display: block; margin-top: var(--space-4); }

/* ── Request-in-flight feedback ── */
button.htmx-request,
.htmx-request button,
input[type="submit"].htmx-request {
  opacity: 0.6;
  cursor: wait;
  pointer-events: none;
}

/* Player headshots hotlinked from school athletics sites. Initials chip is
   layered under the image; a missing/blocked url reveals it via onerror. */
.player-photo {
  display: inline-grid;
  place-items: center;
  vertical-align: middle;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--bg-sunken);
  flex: none;
}
.player-photo > * { grid-area: 1 / 1; }
.player-photo img { width: 100%; height: 100%; object-fit: cover; }
.player-photo__initials {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  letter-spacing: var(--tracking-chip);
  color: var(--text-subtle);
}
.player-photo--lg .player-photo__initials { font-size: var(--size-title-md); letter-spacing: 0; }
.player-photo--sm { width: 28px; height: 28px; }
.player-photo--lg { width: 72px; height: 72px; }
.with-photo .player-photo--sm { margin-right: var(--space-3); }
.with-photo a { font-size: var(--size-body); }
.player-header {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-6);
  align-items: center;
}
.player-header .player-photo--lg { grid-row: 1 / span 3; }
.player-header > :not(.player-photo) { grid-column: 2; }

/* ── Player detail: identity strip ── */
.player-identity { display: flex; align-items: center; gap: var(--space-6); }
.player-identity__text { min-width: 0; }
.player-identity h1 { margin: 0 0 var(--space-2); }
.player-identity p { margin: 0; }
.player-identity p + p { margin-top: var(--space-1); }

/* ── Responsive / mobile pass ─────────────────────────────────────────────── */
@media (max-width: 640px) {
  :root { --sticky-top: 0px; }

  .container { padding: var(--space-6) var(--space-5); }

  /* Wrapped topnav is tall and variable-height — static on mobile so it
     doesn't eat the viewport; nested sticky offsets fall back to 0. */
  .topnav {
    position: static;
    flex-wrap: wrap;
    row-gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
  }
  .nav-links {
    flex-wrap: wrap;
    row-gap: var(--space-2);
    width: 100%;
    order: 3;
  }

  .league-nav { gap: var(--space-1) var(--space-2); }

  /* Tables keep their layout but scroll horizontally instead of squeezing
     columns or overflowing the page. */
  table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
    max-width: 100%;
    -webkit-overflow-scrolling: touch;
  }

  form, .auth-form { max-width: 100%; width: 100%; }
  /* Keep table-cell action forms compact — they should not stretch to the
     full (scrollable) table width just because the page is narrow. */
  td form, th form { width: auto; max-width: none; }

  button, .btn, .notif-mark-read, input[type="submit"] {
    min-height: 44px;
    padding: 10px 16px;
  }
  input:not([type="checkbox"]):not([type="radio"]), select, textarea { min-height: 44px; }

  .notif-panel {
    position: fixed;
    top: 56px;
    right: 8px;
    left: 8px;
    width: auto;
    max-width: none;
  }
}

/* ── Viewport-locked shell ── */
@media (min-width: 1200px) and (min-height: 800px) {
  html, body { height: 100%; overflow: hidden; }

  /* Topnav is its own non-scrolling row now; zeroing the sticky-offset token
     corrects .stat-table thead, .draft-status-pin and .lineup-rail. */
  :root { --sticky-top: 0px; }

  .app-shell { display: flex; flex-direction: column; height: 100dvh; }
  .topnav, .league-nav, .flash-slot { position: static; flex: none; }
  .league-nav { margin: 0; }

  .container {
    flex: 1 1 auto;
    min-height: 0;
    max-width: none;
    /* Base rule's margin: 0 auto centers the column; as a flex item this
       must be zeroed, not just overridden by width, to avoid shrink-wrap. */
    margin: 0;
    padding: var(--space-7) var(--space-8);
    display: flex;
    flex-direction: column;
    /* Safety net for pages with no .fill child; inert when one is present,
       because .fill sizes to exactly the leftover space. */
    overflow-y: auto;
    scrollbar-gutter: stable;
  }
  .container > * { flex: none; }

  /* ── Layout vocabulary ── */

  /* The one child of a region that consumes the leftover height. */
  .fill { flex: 1 1 auto; min-height: 0; min-width: 0; }

  /* A region that owns a scrollbar. `flex` is ignored when it is a grid item. */
  .pane {
    flex: 1 1 auto;
    min-height: 0;
    min-width: 0;
    overflow-y: auto;
    scrollbar-gutter: stable;
  }

  /* A pane inside another pane would give the same content two scrollbars;
     the outer one wins and the inner reverts to ordinary flow. */
  .pane .pane { overflow: visible; flex: none; }

  /* Exception: a nested table-scroll wrapper still owns its x-axis scroll —
     flattening it would let wide tables blow out the pane instead. */
  .pane .table-scroll.pane { overflow-x: auto; }

  /* Column that holds a pane: flex so .pane-head pins and .pane scrolls. */
  .pane-col { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
  /* Flex items default to flex-shrink: 1, which would squash a card header or
     a filter toolbar once the column runs out of room. Everything in a column
     holds its natural height except the parts explicitly told to grow. */
  .pane-col > * { flex: none; }
  .pane-col > .pane, .pane-col > .fill { flex: 1 1 auto; }

  /* Non-scrolling cap on a pane: filter toolbar, section title, tab strip. */
  .pane-head {
    flex: none;
    position: sticky;
    top: 0;
    z-index: 3;
    background: var(--bg-page);
    padding-bottom: var(--space-4);
    margin-bottom: 0;
  }
  /* A pane-head inside a card sits on --bg-surface, not the page. */
  .card > .pane-head,
  .pane-head--surface { background: var(--bg-surface); }
  .tabs.pane-head { background: var(--bg-page); padding: var(--space-4) 0 0; }

  /* minmax(0, …) and the min-* resets on the children are load-bearing:
     without them a wide table sizes its column to content and the pane
     never clips. */
  .col-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .col-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .col-2-rail { display: grid; grid-template-columns: minmax(0, 1fr) 340px; }
  /* Dashboard's Teams rail: 3-column table needs more than the stock rail. */
  .col-2-rail--wide { grid-template-columns: minmax(0, 1fr) 425px; }
  .col-2, .col-3, .col-2-rail {
    gap: var(--space-6);
    min-height: 0;
    align-items: stretch;
  }
  .col-2 > *, .col-3 > *, .col-2-rail > * { min-width: 0; min-height: 0; }
  /* Draft prep only: 7 stat columns in the available-players pool need more
     room than the ranked-queue column — shift the 50/50 split toward the
     pool (~10 percentage points) so stats don't truncate at 1920px. */
  .draft-rankings-split { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr); }

  /* align-items: start (written for the old scrolling page) lets children
     size to content under the lock, so panes never get a bounded height. */
  .lineup-grid, .matchup-split {
    align-items: stretch;
    min-height: 0;
  }
  .lineup-grid > *, .matchup-split > * { min-width: 0; min-height: 0; }

  /* Lineup page is a two-column form, not a full-bleed workspace — wider
     than the old 1080px column so action buttons sit side by side. */
  .lineup-grid {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
  }

  /* ── Reconciling rules written for the scrolling-page era ────────────── */

  /* These pixel caps were standing in for a viewport bound. Their parent pane
     supplies a real one now, and the guesses fight it. */
  .table-scroll.game-log-scroll,
  .table-scroll.rankings-scroll { max-height: none; }

  /* form:not(.auth-form) caps at 512px — that cap is what holds .field-grid
     to two columns. Release it on the forms that now own a full column.
     .auth-form keeps its 384px cap so login inputs do not stretch. */
  .settings-form, .trade-form { max-width: none; }

  /* .auth-form carries its own margin: 0 auto in the base rule now, so it
     stays centered whether .container is a flex column (this lock) or not. */

  /* Sections stack inside a pane now; their own vertical margins double up
     against the pane-col gap. */
  .admin-section { margin: 0 0 var(--space-6); }

  /* Draft room grid: status + recent-picks row on top, four columns below
     (pool/picks/queue/roster rail), pick board full-width strip. */
  .draft-top-row {
    flex: none;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 400px;
    align-items: start;
    gap: var(--space-6);
  }
  .draft-top-row > * { min-width: 0; }
  #draft-recent-picks { max-height: 160px; }
  .draft-room-grid {
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 260px 240px 260px;
    /* Row 1 keeps a real floor even at 800px viewport height; row 2's floor
       and weight shrink so row 1 wins leftover space. A complete draft skips
       row 1 entirely, so the board becomes the tall, fully-read strip. */
    grid-template-rows: minmax(220px, 1fr) minmax(150px, 0.6fr);
    gap: var(--space-6);
    min-height: 0;
  }
  .draft-room-grid > * { min-height: 0; min-width: 0; }
  #available-players { grid-column: 1; grid-row: 1; }
  /* Desktop lock: pool column runs narrow, so tighter cells, truncated
     name+school and hidden secondary stat columns avoid a scrollbar. */
  #available-players .stat-table th,
  #available-players .stat-table td { padding: var(--space-1) var(--space-2); font-size: var(--size-micro); }
  #available-players .player__name,
  #available-players .player__meta {
    display: block;
    max-width: 90px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Under the lock there's no room for search/filters to wrap onto their
     own lines, so force one row and shrink the search box to fit. */
  #available-players .filters {
    flex-wrap: nowrap;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
  }
  #available-players .filters input[type="search"] { flex: 0 1 90px; min-width: 70px; }
  #available-players .position-checks { flex-wrap: nowrap; gap: var(--space-1) var(--space-3); }
  #available-players .pos-check { font-size: var(--size-micro); gap: 2px; }
  /* Card padding and header spacing trimmed to buy the table more of the
     card's own height — at 800px viewport height row 1 sits on its 220px
     floor and every pixel not spent on chrome is a pixel spent on rows. */
  #available-players .card { padding: var(--space-4); }
  #available-players .card__header { margin-bottom: var(--space-3); }
  /* Belt-and-suspenders clip: a table's intrinsic height can blow past its
     grid/flex stretch chain, so this bounds the card's own box regardless.
     #draft-rail excluded — it owns its own scroll region already. */
  #available-players,
  #draft-my-picks,
  #draft-queue-card { overflow: hidden; }
  #draft-my-picks { grid-column: 2; grid-row: 1; }
  #draft-queue-card { grid-column: 3; grid-row: 1; }
  #draft-rail { grid-column: 4; grid-row: 1; }
  #draft-board { grid-column: 1 / -1; grid-row: 2; }
  /* Draft room hero is page-scoped (body:has(#draft-room)) since row 1's
     220px floor needs the title trimmed to title-lg to fit. */
  body:has(#draft-room) .page-head {
    margin-bottom: var(--space-4);
  }
  body:has(#draft-room) .page-head h1 {
    font-size: var(--size-title-lg);
    margin-bottom: 0;
  }
  body:has(#draft-room) .page-head__eyebrow {
    margin-bottom: var(--space-1);
  }
  /* Taller row only pays off with compact pick lines: one line under the
     lock, three stacked lines on the complete-draft recap/mobile stack. */
  .draft-room-grid:not(.draft-room-grid--board-only) #draft-board .team-board-col {
    gap: var(--space-2);
    padding: var(--space-3);
  }
  .draft-room-grid:not(.draft-room-grid--board-only) #draft-board .team-board-pick {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--space-2);
  }
  .draft-room-grid:not(.draft-room-grid--board-only) #draft-board .team-board-pick__name { line-height: 1.2; }
  .draft-room-grid:not(.draft-room-grid--board-only) #draft-board .team-board-pick .player__meta { flex-basis: 100%; }
  /* A complete draft drops the pool and the rail entirely. */
  .draft-room-grid--board-only {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }
  .draft-room-grid--board-only #draft-board { grid-column: 1; grid-row: 1; }

  .game-log th, .game-log td { padding: var(--space-2) var(--space-3); font-size: var(--size-small); }
}

@media (min-width: 1200px) and (max-width: 1599.98px) and (min-height: 800px) {
  #available-players .col-secondary { display: none; }
}

/* 1200-1439px only: pool column still can't fit Season plus the two
   pool-actions buttons, so drop Season and stack the actions vertically. */
@media (min-width: 1200px) and (max-width: 1439.98px) and (min-height: 800px) {
  #available-players .col-tertiary { display: none; }
  #available-players .pool-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--space-1);
    white-space: normal;
  }
  #available-players .pool-actions form + form { margin-left: 0; }
  #available-players .pool-actions .btn { padding: 4px 8px; font-size: var(--size-micro); }
}

/* ── Lineup page opts back out of the lock ── */
@media (min-width: 1200px) and (min-height: 800px) {
  html:has(#lineup-area), body:has(#lineup-area) { height: auto; overflow: visible; }
  /* Topnav is sticky again on a scrolling page, so sticky descendants
     (lineup rail, in-flow table headers) need their offset back. */
  html:has(#lineup-area) { --sticky-top: 64px; }
  body:has(#lineup-area) .app-shell { display: block; height: auto; }
  body:has(#lineup-area) .topnav { position: sticky; top: 0; }
  body:has(#lineup-area) .container {
    display: block;
    max-width: 1320px;
    margin: 0 auto;
    min-height: 0;
    overflow: visible;
  }
  body:has(#lineup-area) .fill,
  body:has(#lineup-area) .pane {
    flex: none;
    min-height: auto;
    overflow: visible;
  }
  /* Undo the lock's stretch/cap: normal top-aligned columns, full container
     width so the rail sits flush at the right content edge. */
  body:has(#lineup-area) .lineup-grid {
    align-items: start;
    max-width: none;
  }
}

/* ── Settings page opts back out of the lock ── */
@media (min-width: 1200px) and (min-height: 800px) {
  html:has(#settings-result), body:has(#settings-result) { height: auto; overflow: visible; }
  html:has(#settings-result) { --sticky-top: 64px; }
  body:has(#settings-result) .app-shell { display: block; height: auto; }
  body:has(#settings-result) .topnav { position: sticky; top: 0; }
  body:has(#settings-result) .container {
    display: block;
    min-height: 0;
    overflow: visible;
  }
  body:has(#settings-result) .fill,
  body:has(#settings-result) .pane {
    flex: none;
    min-height: auto;
    overflow: visible;
  }
}

/* ── League members: commissioner bot-seat control ── */
form.members-bots {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-4);
  max-width: none;
  margin-top: var(--space-5);
}
form.members-bots input[type="number"] { width: 5rem; }
