/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

:root {
  color-scheme: dark light;
  --bg: #111418;
  --surface: #1a1e24;
  --text: #e8eaed;
  --text-dim: #9aa4b2;
  --accent: #22b573;
  --border: #2a2f37;
  --danger: #ff6b6b;
  --warning: #ffd23f;
  /* Status-bar inset -- only ever real installed-to-home-screen, where
     viewport-fit=cover puts the page under it (see below). */
  --safe-top: 0px;
  /* The sticky top bar's own height. The filter bar pins directly below it
     (home/_filters), so both sides read the one number. */
  --app-header-height: calc(3rem + var(--safe-top));
}

/* Installed-to-home-screen only, the top-of-screen twin of .bottom-nav's
   safe-area padding: nothing scrolls away up there any more, so the top bar
   itself has to clear the status bar rather than sit under it. */
@media (display-mode: standalone) {
  :root {
    --safe-top: env(safe-area-inset-top);
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  padding-bottom: 4.5rem; /* room for the fixed bottom nav */
}

/* Pinned to the top of the viewport, above the board's own sticky filter bar
   (z-index 5). A fixed height rather than one grown from its contents, so the
   filter bar's offset can be exactly the header's height -- which also means
   it can't wrap: a long display name ellipses instead. */
.app-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  height: var(--app-header-height);
  padding: 0.75rem 1rem;
  padding-top: calc(0.75rem + var(--safe-top));
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  /* Own layer, same reason as .filters below: a sticky bar rasterized
     against the page tiles repaints at rounded offsets on iOS. */
  transform: translateZ(0);
}

.app-header__brand {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 700;
  white-space: nowrap;
}

/* Icon+wordmark lockup shared by .app-header__brand and .sign-in__title
   (layouts/_brand_toast) -- also the toast for a transient flash. .icon and
   .brand-toast__idle/__message stack on top of each other (grid, one cell)
   so the crossfade never changes the lockup's box size, only what's opaque
   inside it -- no reflow, unlike the old .flash. */
.brand-toast {
  position: relative;
  display: flex;
  align-items: center;
  gap: inherit;
  --toast-duration: 3000; /* ms; header_toast_controller.js reads this so the JS timer and the CSS drain bar can't drift apart */
}

.brand-toast__icon {
  display: grid;
}

.brand-toast__crown,
.brand-toast__status {
  grid-area: 1 / 1;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.brand-toast__status {
  opacity: 0;
  transform: scale(0.6);
}

.brand-toast--notice .brand-toast__crown,
.brand-toast--alert .brand-toast__crown {
  opacity: 0;
}

.brand-toast--notice .brand-toast__status,
.brand-toast--alert .brand-toast__status {
  opacity: 1;
  transform: scale(1);
}

.brand-toast--notice .brand-toast__status {
  color: var(--accent);
}

.brand-toast--alert .brand-toast__status {
  color: var(--danger);
}

.brand-toast__text {
  display: grid;
}

.brand-toast__idle,
.brand-toast__message {
  grid-area: 1 / 1;
  max-width: 12rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: opacity 0.3s ease;
}

.brand-toast__message {
  opacity: 0;
}

.brand-toast--notice .brand-toast__idle,
.brand-toast--alert .brand-toast__idle {
  opacity: 0;
}

.brand-toast--notice .brand-toast__message,
.brand-toast--alert .brand-toast__message {
  opacity: 1;
  color: var(--accent);
}

.brand-toast--alert .brand-toast__message {
  color: var(--danger);
}

/* Visible countdown to the revert -- shrinks from full width to nothing over
   --toast-duration, driven by CSS alone so it can't drift from the timer. */
.brand-toast__drain {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.35rem;
  height: 2px;
  background: var(--accent);
  transform-origin: left;
  animation: brand-toast-drain calc(var(--toast-duration) * 1ms) linear forwards;
}

.brand-toast--alert .brand-toast__drain {
  background: var(--danger);
}

@keyframes brand-toast-drain {
  from { transform: scaleX(1); }
  to { transform: scaleX(0); }
}

.app-header__brand .brand-toast__crown,
.app-header__brand .brand-toast__status {
  width: 1.1rem;
  height: 1.1rem;
}

.brand-title .brand-toast__crown,
.brand-title .brand-toast__status {
  width: 2.1rem;
  height: 2.1rem;
}

.app-header__account {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  font-size: 0.9rem;
  color: var(--text-dim);
  text-decoration: none;
}

/* Shrinks and ellipses rather than wrapping the bar onto a second line --
   the header's height is a fixed offset the filter bar sticks below. */
.app-header__account strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text);
}

.role-badge {
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  padding: 0.15rem 0.4rem;
  border-radius: 0.25rem;
  background: var(--border);
  color: var(--text);
}

.role-badge--admin {
  background: var(--accent);
  color: #fff;
}

.app-main {
  max-width: 40rem;
  margin: 0 auto;
  padding: 1rem;
}

.flash {
  padding: 0.6rem 0.9rem;
  border-radius: 0.4rem;
  margin: 0 0 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
}

.flash--alert {
  border-color: var(--danger);
  color: var(--danger);
}

.flash--warning {
  border-color: var(--warning);
  color: var(--warning);
}

/* Generic transient message (push_notifications_controller's toast()) --
   centered above the fixed bottom nav, fades/rises in and back out. */
.toast {
  position: fixed;
  left: 50%;
  bottom: 5.5rem;
  z-index: 30;
  max-width: calc(100% - 2rem);
  padding: 0.6rem 0.9rem;
  border-radius: 0.4rem;
  background: var(--surface);
  border: 1px solid var(--border);
  font-size: 0.85rem;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, 0.4rem);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.toast--danger {
  border-color: var(--danger);
  color: var(--danger);
}

.toast--visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

.button,
.link-button {
  display: inline-block;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 0.4rem;
  padding: 0.6rem 1rem;
  font-size: 1rem;
  text-decoration: none;
  cursor: pointer;
}

.link-button {
  background: transparent;
  color: var(--text-dim);
  padding: 0;
  font-size: 0.85rem;
  text-decoration: underline;
}

.sign-in {
  min-height: 80vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  text-align: center;
}

/* Development-only escape hatch under the Google button -- deliberately dim
   and unstyled-looking, since it never ships. */
.sign-in__dev-link {
  margin-top: 1.5rem;
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Shared by the sign-in screen and the landing page -- same brand
   lockup (crown + wordmark), each page just sets its own bottom margin. */
.brand-title {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-size: 2.25rem;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -0.02em;
}

/* Sized under the title's own line-height so the crown rides alongside
   the text instead of stretching the header row to fit it. */
.brand-crown {
  width: 2.1rem;
  height: 2.1rem;
  flex-shrink: 0;
}

.sign-in__title {
  margin: 0 0 3rem;
}

/* The one button on the page that's deliberately not .button's solid-accent
   fill (2025 app's card-button treatment) -- a dark bordered pill that
   lifts and picks up the accent color on hover, carrying the real Google
   "G" instead of a plain label. */
.button--google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  min-width: 12.5rem;
  padding: 0.875rem 1.5rem;
  border: 2px solid var(--border);
  border-radius: 0.75rem;
  background: var(--surface);
  color: var(--text);
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
  transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.button--google:hover {
  background: color-mix(in srgb, var(--surface) 100%, white 8%);
  border-color: var(--accent);
  transform: translateY(-1px);
}

.button--google:active {
  transform: translateY(0);
}

.button--google__icon {
  flex-shrink: 0;
}

.marketing {
  margin: 0 auto;
  padding: 3rem 0;
}

.marketing__hero {
  text-align: center;
  margin-bottom: 2.5rem;
}

.marketing__title {
  margin: 0 0 0.5rem;
}

.marketing__tagline {
  color: var(--text-dim);
  font-size: 1.1rem;
  margin: 0;
}

.marketing__section {
  margin-bottom: 2rem;
}

.marketing__section h2 {
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
  margin: 0 0 0.5rem;
}

.marketing__section p {
  line-height: 1.6;
  margin: 0;
}

/* Opening paragraph reads better with more room between lines than the
   rest of the page's tighter copy. Qualified with p -- a bare class here
   would lose the specificity tiebreak to .marketing__section p above,
   regardless of source order (same trap the comment below this warns about). */
.marketing__section p.marketing__lede {
  line-height: 2;
}

/* Landing page's What/How/Why: even vertical rhythm between whatever mix
   of paragraph, diagram, and example board a section holds. */
.marketing__section--intro {
  margin-bottom: 2.75rem;
}

/* Beats .marketing__section p's margin: 0 (same specificity would lose
   the tiebreak to source order otherwise). */
.marketing__section.marketing__section--intro > * + * {
  margin-top: 1.25rem;
}

.marketing__cta {
  text-align: center;
  margin-top: 2.5rem;
}

.marketing__cta .button {
  padding: 0.875rem 2rem;
  font-size: 1.05rem;
  border-radius: 0.75rem;
}

/* The top-of-page CTA -- same green .button, just louder than the
   closing one so it reads as the primary action, not a repeat of it. */
.marketing__cta--top {
  margin-top: 0;
  margin-bottom: 2.75rem;
}

.marketing__cta--top .button {
  padding: 1.1rem 2.75rem;
  font-size: 1.25rem;
  border-radius: 0.85rem;
}

/* Season archives' entry point on the landing page -- a bordered card
   (same language as .marketing__season-card) instead of a small dim
   link, kept last on the page since it's the secondary path. */
.marketing__archive-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.5rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--surface);
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.2s ease;
}

.marketing__archive-link:hover {
  border-color: var(--accent);
}

.marketing__archive-link-body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.marketing__archive-link-title {
  font-weight: 600;
}

.marketing__archive-link-sub {
  color: var(--text-dim);
  font-size: 0.85rem;
}

.marketing__archive-link-arrow {
  color: var(--text-dim);
  font-size: 1.2rem;
  flex-shrink: 0;
}

.marketing--wide {
  max-width: 42rem;
}

.marketing__eyebrow {
  margin: 0 0 1rem;
}

.marketing__eyebrow a {
  color: var(--text-dim);
  font-size: 0.9rem;
  text-decoration: none;
}

.marketing__season-disclaimer {
  color: var(--text-dim);
  font-size: 0.9rem;
  line-height: 1.6;
  margin: 0 0 2rem;
}

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

.marketing__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.marketing__table th,
.marketing__table td {
  padding: 0.6rem 0.75rem;
  text-align: left;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.marketing__table th {
  color: var(--text-dim);
  font-weight: 500;
  text-transform: uppercase;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}

.marketing__table--standings tr:first-child td {
  color: var(--accent);
  font-weight: 600;
}

/* Truncates the name with an ellipsis instead of forcing the whole
   standings table wider than a phone screen -- Score stays on-screen
   without side-scrolling. */
.marketing__table td.marketing__table-player {
  max-width: 6.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Rk only ever holds a single digit -- give Player the room instead. */
.marketing__table-rank {
  width: 1.4rem;
  padding-right: 0.35rem;
}

.marketing__net {
  font-variant-numeric: tabular-nums;
}

.marketing__net--top {
  color: var(--accent);
  font-weight: 600;
}

@media (max-width: 30rem) {
  .marketing__table--weekly .marketing__net {
    font-size: 0.8rem;
  }
}

.marketing__swatch {
  display: inline-block;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  margin-right: 0.5rem;
}

/* Season archives index */
.marketing__season-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.marketing__season-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.25rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--surface);
  text-decoration: none;
  transition: border-color 0.2s ease;
}

.marketing__season-card:hover {
  border-color: var(--accent);
}

.marketing__season-card-year {
  color: var(--text);
  font-weight: 600;
  font-size: 1.1rem;
}

.marketing__season-card-chevron {
  color: var(--text-dim);
  font-size: 1.25rem;
  line-height: 1;
}

/* Charts (season recap) */
.marketing__section--charts {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
}

.marketing__figure {
  margin: 0;
}

.marketing__figure-caption {
  color: var(--text-dim);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 0.75rem;
}

.marketing__chart {
  width: 100%;
  height: auto;
  overflow: visible;
}

.marketing__chart-value,
.marketing__chart-tick {
  fill: var(--text-dim);
  font-size: 12px;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

.marketing__chart-value {
  fill: var(--text);
}

.marketing__chart-gridline {
  stroke: var(--border);
  stroke-width: 1;
}

.marketing__chart-leader {
  stroke: var(--text-dim);
  stroke-width: 1;
}

.marketing__table-toggle {
  margin-top: 1rem;
}

.marketing__table-toggle summary {
  color: var(--text-dim);
  font-size: 0.85rem;
  cursor: pointer;
}

.marketing__table-toggle .marketing__table-wrap {
  margin-top: 0.75rem;
}

/* How It Works figures */
.hiw__weight-diagram {
  display: flex;
  gap: 1rem;
  margin-top: 1.25rem;
}

/* Stay side by side even on narrow phones -- a 12rem flex-basis was forcing
   these to wrap to their own rows below ~26rem. Shrinking together instead
   (flex: 1 1 0, min-width: 0) keeps them on one row; max-width caps how
   wide (not tall/narrow) either card can grow to relative to its own
   content height (~8rem), so the pair reads as a rectangle, not a square. */
.hiw__weight-card {
  flex: 1 1 0;
  min-width: 0;
  max-width: 24rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  background: var(--surface);
}

.hiw__weight-card-title {
  font-weight: 600;
}

.hiw__weight-card-sub {
  color: var(--text-dim);
  font-size: 0.8rem;
}

.hiw__weight-bar {
  display: flex;
  gap: 2px;
  height: 2.5rem;
}

/* Same fill/border/shadow recipe as shared/_chip's tiers (--chip-color /
   --chip-shadow), just stretched into a proportional bar instead of a
   fixed badge -- the weight diagram should read as the same idea as the
   real slot chip, not its own color scheme. */
.hiw__weight-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--chip-shadow);
  border-radius: 0.25rem;
  background: var(--chip-color);
  color: #14171c;
  font-family: "Courier New", monospace;
  font-weight: 800;
  font-size: 0.9rem;
  letter-spacing: -0.03em;
  box-shadow: 2px 2px 0 var(--chip-shadow), inset 0 -3px 0 rgb(0 0 0 / 0.2);
}

.hiw__weight-slot--full {
  flex: 3;
  --chip-color: #ff2e88;
  --chip-shadow: #c41e68;
}

.hiw__weight-slot--major {
  flex: 2;
  --chip-color: #00d9ff;
  --chip-shadow: #0099b8;
}

.hiw__weight-slot--minor {
  flex: 1;
  --chip-color: #ffb800;
  --chip-shadow: #cc8e00;
}

.hiw__timeline {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.75rem;
}

.hiw__timeline-step {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding-top: 1.5rem;
  border-top: 2px solid var(--border);
}

.hiw__timeline-dot {
  position: absolute;
  top: -5px;
  left: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

.hiw__timeline-label {
  font-weight: 600;
  font-size: 0.9rem;
}

.hiw__timeline-detail {
  color: var(--text-dim);
  font-size: 0.8rem;
  line-height: 1.4;
}

/* The scoring walkthrough's equation -- the "3×" is shared/_chip, the same
   slot badge the real app uses, not a marketing-only lookalike. */
.hiw__equation {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding: 0.65rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  font-size: 1.15rem;
  font-weight: 600;
}

.hiw__equation-op {
  color: var(--text-dim);
  font-weight: 400;
}

.hiw__equation-result {
  color: var(--accent);
}

/* A major + minor pick's equations sharing one frame -- one grid across
   both rows (each .hiw__equation drops to display: contents and donates
   its five cells to it) so chip/×/value/=/result line up column by
   column instead of each row centering on its own. */
.hiw__equation-group {
  display: grid;
  grid-template-columns: repeat(5, auto);
  justify-content: center;
  justify-items: center;
  align-items: center;
  column-gap: 0.75rem;
  row-gap: 0.5rem;
  padding: 0.65rem;
  border: 1px solid var(--border);
  border-radius: 0.75rem;
}

.hiw__equation-group .hiw__equation {
  display: contents;
}

@media (max-width: 30rem) {
  .hiw__timeline {
    grid-template-columns: 1fr 1fr;
  }
}

.bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* Installed-to-home-screen only: standalone has no browser chrome to clear
   the home-indicator swipe bar for us, so the nav needs its own bottom
   padding here. Gated to display-mode: standalone so a normal browser tab
   (Safari already keeps content clear of the indicator) is untouched. */
@media (display-mode: standalone) {
  .bottom-nav {
    padding-bottom: env(safe-area-inset-bottom);
  }
}

.bottom-nav__tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  text-align: center;
  padding: 0.6rem 0.25rem;
  color: var(--text-dim);
  text-decoration: none;
}

.bottom-nav__icon {
  width: 1.4rem;
  height: 1.4rem;
}

/* The crown glyph fills its own viewBox edge-to-edge horizontally but only
   ~70% vertically, so at the shared icon size it reads visibly shorter
   than the other four. Sized up to match, but the negative margin keeps
   its layout footprint at the same 1.4rem as its siblings -- rendering it
   bigger without that would push the label below theirs. */
.bottom-nav__icon--crown {
  width: 1.6rem;
  height: 1.6rem;
  margin: -0.1rem 0;
}

.bottom-nav__label {
  font-size: 0.65rem;
}

.bottom-nav__tab--active {
  color: var(--accent);
  font-weight: 600;
}

.week-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.week-header h1 {
  font-size: 1.25rem;
  margin: 0;
}

/* The period label doubles as the button that opens
   shared/_week_picker_dialog (ui.md) -- same look as the plain h1 it
   replaced, just reset out of button chrome. */
.week-header__label {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--text);
  cursor: pointer;
}

.week-header__nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  color: var(--accent);
  text-decoration: none;
  font-size: 1.75rem;
  line-height: 1;
}

.week-header__nav--disabled {
  color: var(--text-dim);
  opacity: 0.5;
}

.week-header__window {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin: 0.25rem 0 1.5rem;
  text-align: center;
}

.empty-state {
  color: var(--text-dim);
  text-align: center;
}

/* An empty board is the whole page, so its message drops clear of the date
   line above it instead of tucking under it. A fraction of the viewport
   rather than what's left of it: it lands high in the blank space, and
   there's no chrome to measure that could add up past a screenful. */
.empty-state--roomy {
  display: grid;
  place-items: center;
  min-height: 40dvh;
}

/* Next/last kickoff in the empty board (home/_board) -- a duration, not a
   clock reading, so no timezone conversion belongs here (countdown_controller
   just diffs two instants). */
.board-countdown {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 1.25rem;
  margin-top: 1rem;
  margin-bottom: 1.5rem;
  padding: 1.75rem 1.5rem;
  border-radius: 0.75rem;
  background: var(--surface);
  box-shadow: inset 0 4px 10px rgba(0, 0, 0, 0.45), inset 0 -1px 0 rgba(255, 255, 255, 0.06);
  text-align: center;
}

.board-countdown__item {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
}

.board-countdown__label {
  color: var(--text-dim);
  font-size: 0.85rem;
  text-align: center;
}

.board-countdown__box {
  display: flex;
  justify-content: center;
  padding: 0.5rem 0.75rem;
  border-radius: 0.6rem;
  background: var(--bg);
}

.board-countdown__value {
  color: var(--text-dim);
  opacity: 0.65;
  font-weight: 800;
  font-size: 1.3rem;
  font-variant-numeric: tabular-nums;
}

/* Stands in for the board when every pick on it is the current user's own
   (home/_board's only_mine) -- a labelled version of the plain border that
   used to sit between My Picks and the board (pre-69fdf02's .home-divider). */
.board-divider {
  margin-top: 1.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.85rem;
  text-align: center;
}

.game-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.5rem;
  margin-bottom: 0.75rem;
}

.game-card--voided {
  opacity: 0.6;
}

.game-card__header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
}

/* The gap belongs to whatever follows the header, not the header itself --
   a Now/Final card with nothing under it (no bets yet) would otherwise
   carry a dead margin-bottom with nothing there to separate from. */
.game-card__header + * {
  margin-top: 0.5rem;
}

/* Shared matchup block (shared/_matchup): away team dim over home team
   bright, kickoff stacked icon+day over time on the right. */
.matchup__teams {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.matchup__away,
.matchup__home {
  font-weight: 600;
  font-size: 0.85rem;
}

.matchup__away {
  color: var(--text-dim);
}

.matchup__home {
  color: var(--text);
}

/* Completed-game score prefix (shared/_matchup) -- fixed to 2 digits'
   width regardless of the actual score, so both team names still start
   at the same x position whether it's "7" or "17". */
.matchup__score {
  display: inline-block;
  min-width: 2ch;
  margin-right: 0.5rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Game card only -- team names sized to match the bet description on a
   pick card (.slot-card__selection). */
.game-card__header .matchup__away,
.game-card__header .matchup__home {
  font-size: 1.05rem;
}

/* Pick cards -- both team names read as the same dim byline, no
   home/away emphasis (that distinction is the game card's job). */
.slot-card__meta .matchup__away,
.slot-card__meta .matchup__home {
  color: var(--text-dim);
}

/* Dim, lightly tracked, but sized to match a pick card's team names
   (.matchup__away/__home's base size) -- same row, same two-line stack,
   no reason for the kickoff half to read smaller. One size everywhere,
   including the game card, rather than scaling up with that header's
   bigger team names. */
.matchup__when {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.1rem;
  color: var(--text-dim);
  font-size: 0.85rem;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

.matchup__day {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

/* The icon reads heavier than text at the same size, so it sits a step
   below the day/time text next to it rather than matching it exactly. */
.matchup__day .sport-icon {
  font-size: 0.75rem;
}

/* Home's Live tab (shared/_matchup, state: :live) -- the one place on the
   board something is actually happening right now, so the clock gets the
   app's accent color instead of the usual dim kickoff text. */
.matchup__day--live {
  color: var(--accent);
  font-weight: 600;
}

/* FINAL tab -- bolded like the live clock, but left in the dim/neutral
   palette: the game isn't happening anymore, it's just done. */
.matchup__day--final {
  font-weight: 600;
}

.game-card__voided-note {
  color: var(--danger);
  margin: 0;
}

.game-card__grid {
  display: grid;
  grid-template-columns: 1fr repeat(3, minmax(3rem, 1fr));
  align-items: center;
  gap: 0.1rem;
}

/* A final game's own grid (weeks/_final_odds) -- collapsed by default, a
   plain <details> instead of a Stimulus toggle since there's no state
   beyond open/closed. */
.game-card__odds > .game-card__grid {
  margin-top: 0.5rem;
}

.game-card__odds-toggle {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  width: fit-content;
  color: var(--text-dim);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}

.game-card__odds-toggle::-webkit-details-marker {
  display: none;
}

.game-card__odds-chevron {
  width: 0.7rem;
  height: 0.7rem;
  transition: transform 0.15s ease;
}

.game-card__odds[open] .game-card__odds-chevron {
  transform: rotate(180deg);
}

/* Every player's bets on a Now/Final game (weeks/_game), stacked under its
   matchup header. Own gap instead of .slot-card's usual margin-bottom --
   nested a level deeper here, so the last one shouldn't leave a trailing
   gap the way a standalone list of them does. */
.game-card__picks {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-top: 0.5rem;
}

.game-card__picks .slot-card {
  margin-bottom: 0;
}

.game-card__col-label {
  font-size: 0.65rem;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  text-align: center;
}

.game-card__team {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-dim);
}

.market-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  width: 100%;
  min-height: 3rem;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  padding: 0.3rem 0.2rem;
  color: var(--text);
  font-family: inherit;
  cursor: pointer;
}

.market-btn__price {
  font-size: 0.95rem;
  font-weight: 700;
}

.market-btn__point {
  display: block;
  min-height: 1.1em;
  font-size: 0.75rem;
  color: var(--text-dim);
}

.market-btn--locked {
  opacity: 0.5;
  cursor: default;
}

.market-btn--empty {
  color: var(--text-dim);
}

/* Marks a market as the walkthrough's pick (weeks/_game's selected_market) --
   full opacity even when also --locked, so it reads as chosen, not dimmed. */
.market-btn.market-btn--selected {
  opacity: 1;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 18%, var(--bg));
}

/* Final-game odds grid (weeks/_final_odds): the winning side(s) get a
   tinted-lift look -- a push highlights both
   (Game::Gradeable#market_result). Full opacity even though --locked, same
   reason as --selected above. */
.market-btn.market-btn--won {
  opacity: 1;
  background: color-mix(in srgb, var(--accent) 10%, var(--bg));
  border-color: var(--accent);
  box-shadow: 0 3px 0 0 var(--accent);
  transform: translateY(-2px);
}

/* Same period title as Week's, in the same place: centered, and given the
   height of that page's nav buttons so it doesn't sit higher here. */
.home-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 2.5rem;
  margin-bottom: 0.5rem;
}

.home-header h1,
.home-header h2 {
  font-size: 1.25rem;
  margin: 0;
}

/* home/_tabs -- sits above the (also sticky) filter bar, so it stays in
   normal flow rather than pinning itself; only .filters pins. */
/* A segmented control, not the sport/conference toggle's outlined pills --
   three equal-width slots sharing one rounded track, so the selected tab
   reads as a filled position on a track rather than one more button in a
   row of them. Overrides .toggle-btn__label/:checked's shared rules
   (application.css's toggle-group section) rather than a second copy of
   that structure -- only the skin differs, not the underlying radio markup. */
.board-tabs {
  display: flex;
  gap: 0.15rem;
  margin-bottom: 0.3rem;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  border-radius: 0.5rem;
}

.board-tabs .toggle-btn {
  flex: 1;
}

.board-tabs .toggle-btn__label {
  justify-content: center;
  width: 100%;
  height: 2.1rem;
  padding: 0;
  border: none;
  border-radius: 0.5rem;
  font-weight: 600;
}

.board-tabs .toggle-btn__input:checked + .toggle-btn__label {
  background: var(--accent);
  color: var(--bg);
}

/* M10 #9 -- small, out of the way of the board above it. */
.home-footnote {
  margin-top: 1.75rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 0.8rem;
  text-align: center;
}

.home-footnote a {
  color: var(--text-dim);
}

.home-footnote p {
  margin: 0.25rem 0;
}

/* Vendored Font Awesome glyphs (IconHelper) -- sized/colored like the icon
   font they replaced: a 1em box that fills with the surrounding text color. */
.icon {
  width: 1em;
  height: 1em;
  fill: currentColor;
  flex: none;
  vertical-align: -0.125em;
}

/* Shield (NFL) / football (NCAAF) glyph, sitting in every matchup block's
   kickoff corner and the home sport filter where nothing's picked yet. */
.sport-icon {
  margin-right: 0.3em;
  color: var(--text-dim);
}

/* No text follows it (the sport filter's icon-only NFL/NCAAF buttons) --
   the default right margin above assumes text follows and otherwise pushes
   the glyph off-center in its button. */
.sport-icon--standalone {
  margin-right: 0;
}

/* Team helmet (TeamIconHelper): shell recolored per team (inline style,
   fill: currentColor below), detail/outline fixed to theme tokens so they
   read against either color-scheme. A greyish shell (team-helmet--grey-shell,
   e.g. the Raiders' silver) would swallow the fixed grey detail, so that
   case swaps it to plain white instead. */
.team-helmet {
  margin-right: 0.3em;
}

.team-helmet__shell {
  fill: currentColor;
}

.team-helmet__detail {
  fill: var(--text-dim);
}

.team-helmet__outline {
  fill: var(--border);
}

.team-helmet--grey-shell .team-helmet__detail {
  fill: #fff;
}

/* shared/_matchup: the helmet's own width + margin, so a team with no icon
   still lines its name up with one that has it. */
.matchup__team--indent {
  padding-left: 1.3em;
}

/* The pick-config modal -- centered on the page (a bottom sheet was tried
   first; feedback was it should sit in the middle), via a native <dialog>'s
   own inset:0 + margin:auto centering rather than fighting it. */
.sheet {
  position: fixed;
  inset: 0;
  margin: auto;
  width: min(92vw, 26rem);
  max-height: 85vh;
  border: none;
  border-radius: 0.75rem;
  padding: 0;
  background: var(--surface);
  color: var(--text);
}

.sheet::backdrop {
  background: rgb(0 0 0 / 0.5);
}

.sheet__content {
  padding: 1.25rem;
  overflow-y: auto;
}

.sheet__label {
  font-weight: 600;
  margin: 0 0 0.75rem;
}

/* Pick dialog only -- the tapped game card's own matchup row (shared/
   _matchup), cloned in by pick_form_controller rather than re-rendered, so
   it stays home-bright/away-dim like the card instead of the uniform-dim
   treatment .slot-card__meta gives a settled pick's byline. Same border-top
   divider as .slot-card__meta (a real pick card's own header/matchup
   split), kept separate so that uniform-dim rule doesn't reach in here. */
.sheet__matchup {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  margin: 0.5rem 0 0.75rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

.sheet fieldset {
  border: none;
  padding: 0;
  margin: 0.75rem 0;
}

.sheet legend {
  font-size: 0.75rem;
  color: var(--text-dim);
  padding: 0;
  margin-bottom: 0.4rem;
}

/* Same look as .sheet legend, but a plain paragraph rather than a <legend>
   inside a fieldset -- in the pick dialog, a legend participating in the
   reveal row's own flex layout was the thing making Kickoff/Now a
   different height than Place pick beside them. Also backs the
   week-picker dialog's "Week" label, which has no fieldset at all. */
.sheet__section-label {
  font-size: 0.75rem;
  color: var(--text-dim);
  margin: 0 0 0.4rem;
}

/* Week-picker dialog's own "Week" label -- bumped up and centered, unlike
   the dim compact .sheet__section-label it's based on, since here it's the
   dialog's only heading rather than a caption above a fieldset. */
.sheet--week-picker .sheet__section-label {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--text);
  text-align: center;
  margin-bottom: 0.75rem;
}

/* Week-picker dialog (shared/_week_picker_dialog): every period in the
   season, five to a row -- roughly a season's worth of weeks (~25) fits in
   five rows, number only, no separate label per button. */
.week-picker__grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.5rem;
}

.week-picker__option {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--text);
  text-decoration: none;
  font-size: 0.95rem;
}

/* Browser default focus ring lands on the first link in the dialog unless
   something else claims autofocus (the view gives that to --viewing) --
   still worth overriding so a keyboard user sees the app's own accent
   rather than a mismatched blue outline. --viewing suppresses it below --
   its own border already says "this one", so the ring would just double it. */
.week-picker__option:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The week actually on screen right now (current_period) -- one border,
   nothing else; --viewing already autofocuses on open (the view), so its
   own :focus-visible ring is suppressed rather than stacking a second one
   on top of this border. */
.week-picker__option--viewing {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

.week-picker__option--viewing:focus-visible {
  outline: none;
}

/* The real live week (today_period), regardless of which one's being
   browsed -- green number over a faint green tint, independent of
   --viewing's border above (the two combine when they're the same week). */
.week-picker__option--today {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
  font-weight: 600;
}

/* Hasn't started yet -- still a plain link (nothing here is disabled), just
   dimmer so it reads as "not yet" rather than "same as any other week".
   :not(--viewing) so browsing ahead to a future week still shows that
   week's own accent border rather than losing it to the dimming. */
.week-picker__option--future {
  border-style: dashed;
}

.week-picker__option--future:not(.week-picker__option--viewing) {
  color: var(--text-dim);
}

/* Pick dialog only -- reveal choice and submit share one row (Kickoff/Now
   read short enough to sit beside Place pick without wrapping), rather than
   a separate actions row below. Stretched, not centered, so the toggle
   fieldset and Place pick fill the same height as each other. */
.sheet__reveal-row {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.75rem;
}

.sheet__reveal-row fieldset {
  margin: 0;
}

.sheet__reveal-row .toggle-btn__label {
  height: 100%;
  box-sizing: border-box;
}

/* Place pick only -- the chip's own bevel (offset hard shadow, thick
   border, inset highlight) instead of .button's flat fill, so the app's
   one solid-green surface reads as the same tactile sprite language as the
   chips above it rather than a plain SaaS button. Presses flat on tap. */
.button--chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: #0c1f16;
  font-weight: 800;
  border: 2px solid color-mix(in srgb, var(--accent) 55%, black);
  border-radius: 0.4rem;
  box-shadow: 3px 3px 0 color-mix(in srgb, var(--accent) 55%, black), inset 0 -4px 0 rgb(0 0 0 / 0.25);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

/* The chevron_down glyph (IconHelper) pointing right instead -- rather than
   vendor a second arrow icon just for this one button. .icon's own 1em
   default sits taller than the label's cap-height and reads thin next to
   its 800 weight, so this both shrinks it to match and thickens the glyph
   with a matching stroke (the fill path alone is too fine at any size). */
.button--chip__chevron {
  width: 0.65em;
  height: 0.65em;
  stroke: currentColor;
  stroke-width: 40px;
  stroke-linejoin: round;
  transform: rotate(-90deg);
}

.button--chip:active {
  transform: translateY(2px);
  box-shadow: 1px 1px 0 color-mix(in srgb, var(--accent) 55%, black), inset 0 -2px 0 rgb(0 0 0 / 0.25);
}

.sheet__actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem;
  margin-top: 0.75rem;
}

/* Up to three stacked rows: board tabs, the sport row, and the conference
   row (each conditionally rendered -- home/_filters). Sticky, not fixed: it
   sits in normal flow until the board scrolls past it, then pins directly
   below the sticky app header, so finding a team never means scrolling back
   up. Same inset, padding, and radius as .game-card below it, so it reads as
   a bounded bar of the same kind rather than a full-bleed strip. */
.filters {
  position: sticky;
  top: var(--app-header-height);
  z-index: 5;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-bottom: 1rem;
  padding: 0.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  box-shadow: 0 4px 10px rgb(0 0 0 / 0.4);
  /* Own compositing layer: iOS paints a sticky bar at a fractional scroll
     offset but rounds repaint rects, which left a stale sliver of the old
     pill's green border behind when the selection changed. */
  transform: translateZ(0);
}

/* Once pinned directly under .app-header (filters_controller), merge into
   one seamless toolbar instead of two separate bars: full-bleed and square
   again (undoing the resting state's inset + rounding above) so it's flush
   with the header's own edges, no border on the sides that now run off the
   viewport edge nor the top (the header sits flush above it), and a deeper
   shadow closes the merged block's bottom edge against the board scrolling
   past beneath it. .app-header's own rule is untouched -- the :has() below
   only overrides its rendered border while this class is present, canceling
   the seam that would otherwise sit between the two. */
.filters--stuck {
  margin-left: -1rem;
  margin-right: -1rem;
  padding-left: 1rem;
  padding-right: 1rem;
  border-top-color: transparent;
  border-left: none;
  border-right: none;
  border-radius: 0;
  box-shadow: 0 6px 16px rgb(0 0 0 / 0.45);
}

body:has(.filters--stuck) .app-header {
  border-bottom-color: transparent;
}

/* One row: two half-width sport buttons (home/_sport_button) splitting it
   evenly. */
.sport-filter {
  display: flex;
  gap: 0.5rem;
}

/* Half-width, sharing .board-tabs' own height/radius family (item 2,
   specs/ui.md) so the two sticky rows read as a matched pair. */
.sport-btn {
  flex: 1 1 0;
  min-width: 0;
}

/* Sits between the NFL/CFB pills (weeks/_filters), same row height as its
   siblings but deliberately card-styled rather than pill-shaped -- unlike
   them it doesn't filter this page, it jumps to a different week (opens
   shared/_week_picker_dialog). Content-sized (flex: 0 0 auto), not sharing
   the row 1:1:1 with NFL/CFB the way they share with each other -- it's a
   shortcut, not a peer control, so it stays only as wide as "Week N" needs;
   NFL/CFB's own flex: 1 1 0 splits whatever width that leaves them. Hidden
   until the bar's actually pinned (.filters--stuck): unpinned, the header
   above it already has its own week switcher a glance away, so this would
   just be a second one competing for the same job; once the bar's stuck the
   header's scrolled out of reach and this becomes the only nearby way to
   jump weeks. */
.week-filter-btn {
  display: none;
  flex: 0 0 auto;
  height: 2.1rem;
  padding: 0 0.65rem;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  background: var(--surface);
  color: var(--text);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

.filters--stuck .week-filter-btn {
  display: block;
}

/* Selected style shared by all three home filter rows -- game state
   (.board-tabs, above), sport (.sport-filter), and conference/division
   (.conference-filter, below) -- so they read as one matched family rather
   than three different controls. Board tabs paint this via their own track
   background; these two lay it on each pill directly since they don't share
   one. */
.sport-filter .toggle-btn__label,
.conference-filter .toggle-btn__label {
  height: 2.1rem;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  border: none;
  border-radius: 1.05rem;
}

.sport-filter .toggle-btn__input:checked + .toggle-btn__label,
.conference-filter .toggle-btn__input:checked + .toggle-btn__label {
  background: var(--accent);
  border-color: transparent;
  color: var(--bg);
}

.sport-filter .toggle-btn__label {
  width: 100%;
  justify-content: center;
}

/* Follows the label's own color (dim, then dark once selected above)
   instead of .sport-icon's fixed dim default -- otherwise the icon stays
   dim against the selected pill's dark text, reading like it's still off. */
.sport-filter .sport-icon {
  color: inherit;
}

/* Its own row, scrolling sideways instead of wrapping -- the conference
   count swings week to week, so this can't share the sport row's fixed
   layout above it. Hidden until a sport's picked (filters_controller), and
   narrowed to that sport's own pills from there. */
.conference-filter {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.4rem;
  overflow-x: auto;
  scrollbar-width: none;
}

.conference-filter[hidden] {
  display: none;
}

.conference-filter::-webkit-scrollbar {
  display: none;
}

.conference-filter .toggle-btn {
  flex: 0 0 auto;
}

.conference-filter .toggle-btn__label {
  padding: 0 0.75rem;
  font-size: 0.8rem;
}

/* A button-group radio: the label itself is the aesthetic control, a plain
   radio sits underneath (opacity: 0, but real size/position) for native
   form semantics and keyboard/Capybara interaction. Shared by the sport
   filter and the pick dialog's reveal choice, so this pattern lives once. */
.toggle-group {
  display: flex;
  gap: 0.4rem;
}

.toggle-btn {
  position: relative;
}

.toggle-btn__input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.toggle-btn__label {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  padding: 0.4rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  font-size: 0.8rem;
  color: var(--text-dim);
  white-space: nowrap;
}

/* Outline + text only, no tinted fill -- keeps green meaning one thing in
   the pick dialog (the "Place pick" button, the one solid-green surface)
   instead of also filling every selected pill. */
.toggle-btn__input:checked + .toggle-btn__label {
  border-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}

/* Reveal choice icons (lock/eye, RevealIconHelper) -- currentColor so they
   pick up the label's own color, dim when unselected and green when
   checked, same as the text beside them. */
.toggle-btn__icon {
  width: 1rem;
  height: 1rem;
  flex: none;
}

/* Reserve two digits' width so the sport toggle doesn't shift size as the
   filtered count changes (e.g. dropping to a single digit). */
.toggle-btn__count {
  display: inline-block;
  min-width: 2ch;
  text-align: center;
}

.slot-picker {
  display: flex;
  gap: 0.5rem;
  justify-content: center;
}

.slot-picker__option {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0.5rem;
  border-radius: 0.5rem;
  cursor: pointer;
}

/* Hidden but real (opacity, not display:none) -- the chip is the aesthetic
   control; this just keeps native radio semantics/keyboard behavior. */
.slot-picker__input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

/* Selected state: bigger + a deeper drop shadow, same offset-sprite style as
   the resting chip, rather than a ring around it -- a ring sat outside the
   chip's own offset shadow and read as misaligned with it. */
.slot-picker__input:checked ~ .chip {
  transform: translateY(-2px) scale(1.15);
  box-shadow: 5px 7px 0 var(--chip-shadow), inset 0 -4px 0 rgb(0 0 0 / 0.2);
}

.slot-picker__label {
  font-size: 0.8rem;
  /* Room for the checked chip's scale-up above (:checked ~ .chip) to grow
     into without covering the label. */
  padding-top: 0.35rem;
}

/* All-in/major/minor badge (ui.md) -- the multiplier itself is the sprite,
   bold and color-coded per tier, everywhere the slot choice shows up: the
   pick dialog's slot picker, My Picks' slot cards, the board. */
.chip {
  --chip-color: var(--text-dim);
  --chip-shadow: #14171c;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.2rem;
  height: 2.2rem;
  padding: 0 0.35rem;
  background: var(--chip-color);
  color: #14171c;
  font-family: "Courier New", monospace;
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: -0.03em;
  border: 2px solid var(--chip-shadow);
  border-radius: 0.25rem;
  box-shadow: 2px 2px 0 var(--chip-shadow), inset 0 -3px 0 rgb(0 0 0 / 0.2);
  transition: transform 0.12s ease, box-shadow 0.12s ease;
}

/* Fill + a matching darker shadow per tier -- hot pink/cyan/gold reads more
   distinctly at a glance than the previous gold/violet/green, and each
   tier's shadow is a shade of its own fill rather than the flat near-black
   every chip used before. */
.chip--all_in {
  --chip-color: #ff2e88;
  --chip-shadow: #c41e68;
}

.chip--major {
  --chip-color: #00d9ff;
  --chip-shadow: #0099b8;
}

.chip--minor {
  --chip-color: #ffb800;
  --chip-shadow: #cc8e00;
}

/* Leaderboard standings' rank 1-3, full size -- same chip shape as the
   tiers above, medal tones instead. */
.chip--gold {
  --chip-color: #f1c542;
  --chip-shadow: #b3861a;
}

.chip--silver {
  --chip-color: #c7ccd6;
  --chip-shadow: #868c99;
}

.chip--bronze {
  --chip-color: #cd8c52;
  --chip-shadow: #8f5d2e;
}

/* The pick row's compact chip (shared/_chip size: "sm") -- the row already
   has a name/description doing most of the talking, so the slot badge
   shrinks to match instead of anchoring the row's height. */
.chip--sm {
  min-width: 1.5rem;
  height: 1.5rem;
  font-size: 0.65rem;
  border-width: 1px;
  box-shadow: 1px 1px 0 var(--chip-shadow), inset 0 -2px 0 rgb(0 0 0 / 0.2);
}

.chip--lg {
  min-width: 3.4rem;
  height: 3.4rem;
  font-size: 1.4rem;
  box-shadow: 3px 3px 0 var(--chip-shadow), inset 0 -4px 0 rgb(0 0 0 / 0.2);
}

/* Leaderboard rank badge: same fill/border as the square pick-slot chip,
   but flatter -- the square's inset bevel highlight is built for straight
   edges and reads as a stray band once clipped to a circle, so this drops
   it for a plain offset shadow. Bigger than the square's own sm size so it
   still reads as a badge, not a dot. */
.chip--circle {
  min-width: 2rem;
  height: 2rem;
  border-radius: 50%;
  box-shadow: 2px 2px 0 var(--chip-shadow);
}

/* Three-segment progress toward this week's picks being placed
   (shared/_pick_progress), between the week selector and My Picks. One
   thin row -- a status glance, not another card. */
.pick-progress {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.pick-progress__bar {
  display: flex;
  flex: 1;
  gap: 0.25rem;
}

.pick-progress__segment {
  flex: 1;
  height: 0.3rem;
  border-radius: 0.15rem;
  background: color-mix(in srgb, var(--text) 12%, transparent);
}

.pick-progress__segment--filled {
  background: var(--accent);
}

.my-slots {
  margin-bottom: 0.75rem;
}

.board {
  margin-bottom: 1.5rem;
}

.my-slots h2,
.board h2 {
  font-size: 1rem;
  margin-bottom: 0.5rem;
}

.slot-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.5rem;
  margin-bottom: 1rem;
}

/* Placeholder for a slot you haven't filled yet (shared/_empty_slot_card)
   -- same corner radius and roughly the same height as a real .slot-card,
   so My Picks doesn't reflow once a pick lands here. A plain link now
   (picking happens on the Games tab, not here), but keeps its original
   dashed-placeholder look rather than reading as a nav control. */
.empty-slot-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  width: 100%;
  min-height: 5.5rem;
  margin-bottom: 0.5rem;
  background: none;
  border: 1px dashed var(--border);
  border-radius: 0.5rem;
  color: var(--text-dim);
  text-decoration: none;
  cursor: pointer;
}

.empty-slot-card__icon {
  width: 1.4rem;
  height: 1.4rem;
}

.empty-slot-card__label {
  font-size: 0.85rem;
  font-weight: 500;
}

.slot-card__header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Board-only (picks/_card show_name): whose card this is, its own row
   above the selection -- Kickoff sort only, Player sort's own group
   divider already says who (see the data-sort-mode rule below). */
.slot-card__player-tag {
  display: inline-flex;
  margin-bottom: 0.5rem;
}

/* Plain text while pending; once settled it also carries the chip as a
   prefix ("2× Lions -3.5"), so this is a flex row throughout. One line
   only -- a long team name ellipses in .slot-card__selection-head rather
   than wrapping, so the row never grows past its single-line height. */
.slot-card__selection {
  display: flex;
  gap: 0.25rem;
  flex: 1 1 auto;
  min-width: 0;
  color: var(--text);
  font-weight: 700;
  font-size: 1.05rem;
  align-items: center;
}

/* The team name (or "Over"/"Under") -- shrinks and ellipses first so the
   tail beside it never gets pushed off. */
.slot-card__selection-head {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  min-width: 0;
}

/* "ML" or the spread/total number -- never shrinks or wraps away from
   the team name it qualifies. */
.slot-card__selection-tail {
  flex: none;
  white-space: nowrap;
}

/* Someone else's unrevealed pick, face-down on the board -- dimmed and
   de-emphasized next to real selections. */
.slot-card__selection--hidden {
  color: var(--text-dim);
  font-weight: 500;
}

/* Chip × base points, sized to sit side by side. */
.slot-card__points {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.slot-card__base {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  line-height: 1.1;
}

.slot-card__base-value {
  font-weight: 700;
  font-size: 1.3rem;
}

/* Settled state: just the final points (the chip moved in front of the
   selection instead) -- the color alone carries a win/push's result. Sized
   like the selection text beside it (.slot-card__selection) rather than
   the pending side's bigger .slot-card__base-value, which only ever shows
   up alongside a chip multiplying it -- a bare settled number doesn't need
   that same weight. A loss swaps the number for a LOSS label instead
   (.slot-card__result-label below), since a point value of zero reads
   oddly as a number. */
.slot-card__points--settled {
  display: flex;
  align-items: center;
}

.slot-card__settled-value {
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--result-color);
}

/* A numeral reads visually smaller than the bold selection text beside it
   at the same nominal size, so the win value gets a step up to actually
   match it -- push/void's own settled-value is left at the base size. */
.slot-card__points--win .slot-card__settled-value {
  font-size: 1.15rem;
}

.slot-card__points--win {
  --result-color: #4caf7d;
}

.slot-card__points--loss {
  --result-color: var(--danger);
}

/* Sized like the matchup's own day/time text (.matchup__when) rather than
   .slot-card__base-value's big point-value weight, so a loss doesn't loom
   next to an actual win's number. */
.slot-card__result-label {
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--result-color);
}

.slot-card__points--push,
.slot-card__points--void {
  --result-color: var(--warning);
}

/* Dashed, not solid -- distinct from the meta row's own solid separator
   above it (game info vs. pick editor/metadata are a lighter split than
   header vs. game info) -- and the extra margin/padding around the line
   is what actually pulls the footer away from the matchup above it. */
.slot-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 0.75rem;
  padding-top: 0.5rem;
  border-top: 1px dashed var(--border);
}

.slot-card__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* Icon-only outline squares -- Edit (dim) and Remove (danger); aria-label/
   title carry the text. */
.slot-card__action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  background: none;
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  color: var(--text-dim);
  font-size: 0.8rem;
  cursor: pointer;
}

.slot-card__action--danger {
  color: var(--danger);
}

.slot-card__reveal {
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Hidden pick's header-row stand-in for .slot-card__meta (shown only in
   compact mode, where the meta row itself is hidden below) -- one line,
   smaller than the matchup's own day/time text, so it fits the header's
   existing height. */
.slot-card__hidden-meta {
  display: none;
  align-items: center;
  gap: 0.3rem;
  flex: none;
  color: var(--text-dim);
  font-size: 0.75rem;
  white-space: nowrap;
}

.slot-card__hidden-meta .sport-icon {
  font-size: 0.7rem;
}

.slot-card[data-compact="true"] .slot-card__hidden-meta {
  display: inline-flex;
}

.slot-card__meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

/* Paired with .slot-card__meta above (same box, and its compact-mode
   hide rule) on a legacy pick card: no live Game to render shared/matchup,
   just the bet type and its price, so only the text treatment differs. */
.legacy-pick__meta {
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Compact mode (compact_controller): hides a card's own team/kickoff row.
   data-compact lives on the card itself, not an ancestor -- the toggle sets
   it on every card in its section, but a tap can then flip just one, so
   Home's own slot cards (never a compact_controller target) are untouched
   either way. */
.slot-card[data-compact="true"] .slot-card__meta {
  display: none;
}

/* Only cards a tap can actually flip (Week board, Profile history) hint at
   it -- Home's slot cards have their own Edit/Remove footer instead. */
.board .slot-card,
.profile-history .slot-card {
  cursor: pointer;
}

/* Two-icon segmented control (Week board/Profile/Season Archive pick-card
   density) -- shows, rather than tells, what each side switches the cards
   to. Always exactly one radio checked, so one circle always reads
   "selected". */
/* Track container matches .board-sort__group's family -- flush pills, no
   padding, so the checked option's fill reaches the track's own edges. */
.density-toggle {
  display: inline-flex;
  gap: 0.15rem;
  margin-left: auto;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  border-radius: 1rem;
}

.density-toggle__option {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 2rem;
  padding: 0 0.5rem;
  border-radius: 1rem;
  color: var(--text-dim);
  cursor: pointer;
}

.density-toggle__option:has(.density-toggle__input:checked) {
  background: var(--accent);
  color: var(--bg);
}

.density-toggle__option:has(.density-toggle__input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.density-toggle__input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.density-toggle__glyph {
  width: 1.4rem;
  height: 1.4rem;
}

/* Settings' Notifications switch (push_notifications_controller) -- same
   hidden-checkbox-plus-track-thumb recipe as .compact-toggle, its own
   block since it lives in a setting-card__row rather than a sort row. */
.notify-toggle {
  position: relative;
  display: inline-flex;
  flex: none;
  cursor: pointer;
}

.notify-toggle__input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

.notify-toggle__track {
  position: relative;
  flex: none;
  width: 2rem;
  height: 1.1rem;
  background: var(--border);
  border-radius: 999px;
  transition: background 0.15s ease;
}

.notify-toggle__thumb {
  position: absolute;
  top: 0.1rem;
  left: 0.1rem;
  width: 0.9rem;
  height: 0.9rem;
  background: var(--surface);
  border-radius: 50%;
  transition: transform 0.15s ease;
}

.notify-toggle__input:checked + .notify-toggle__track {
  background: var(--accent);
}

.notify-toggle__input:checked + .notify-toggle__track .notify-toggle__thumb {
  transform: translateX(0.9rem);
}

.notify-toggle__input:focus-visible + .notify-toggle__track {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Same bordered-surface-card look as .filters (Home) -- not sticky, just
   the border/background/radius/shadow, so the two read as the same family
   of container even though only one of them pins. */
.board-sort {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
  padding: 0.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  box-shadow: 0 4px 10px rgb(0 0 0 / 0.4);
  font-size: 0.85rem;
  color: var(--text-dim);
}

.board-sort__label {
  display: inline-block;
}

/* Same rounded-track/filled-segment family as .board-tabs (Home filter
   bar's game-state toggle), just inline-width instead of stretched
   full-row. */
.board-sort__group {
  display: inline-flex;
  gap: 0.15rem;
  background: color-mix(in srgb, var(--text) 8%, transparent);
  border-radius: 0.95rem;
}

.board-sort__tab {
  position: relative;
}

.board-sort__input {
  position: absolute;
  opacity: 0;
  inset: 0;
  margin: 0;
  cursor: pointer;
}

/* Also used as a plain link (Profile/Season Archive sort -- real
   navigation, no radio/JS behind it) -- same pill look either way. */
.board-sort__text {
  display: inline-flex;
  align-items: center;
  height: 1.9rem;
  padding: 0 0.75rem;
  border-radius: 0.95rem;
  color: inherit;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
}

.board-sort__input:checked + .board-sort__text,
.board-sort__text--active {
  background: var(--accent);
  color: var(--bg);
  font-weight: 600;
}

/* Group divider (shared/_group_divider): a centered pill on a full-width
   rule, marking a group boundary wherever the app groups things -- the
   board's Kickoff (day) and Player (name) sort, Profile's week grouping.
   One shared form so grouping looks and behaves the same everywhere. */
.group-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0 0.75rem;
}

.group-divider__rule {
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* pill: false's plain alternative to .value-pill below -- same size/weight,
   no background/padding, for a value (a day, a week number) that isn't a
   name anywhere else on the card. */
.group-divider__label {
  color: var(--text-dim);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

/* The small, rounded label used by .group-divider's pill, a board card's
   own player tag (.slot-card__player-tag), and a leaderboard row's name
   (.standings-card__name) -- one shape for "whose name this is", sized to
   match the team names it sits beside (.matchup__away/__home). */
.value-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.6rem;
  border-radius: 0.25rem;
  background: var(--border);
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
}

/* The board's two sort tabs share one fully-rendered set of cards
   (sort_controller only reorders/regroups, never re-renders) -- .board's
   own data-sort-mode (kickoff by default, matching the checked radio;
   sort_controller updates it on switch) hides each card's player tag once
   Player sort's own group divider says who instead; the card's matchup
   block carries kickoff under either sort. */
.board[data-sort-mode="player"] .slot-card__player-tag {
  display: none;
}

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

/* Recent syncs' OK column, condensed to a dot so it fits before "When" on mobile. */
.sync-status {
  display: inline-block;
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
}

.sync-status--ok {
  background: var(--accent);
}

.sync-status--error {
  background: var(--danger);
}

/* Tighter cell padding on mobile so the status dot column doesn't eat
   width that the timestamp/counts need. */
@media (max-width: 480px) {
  .sync-table th,
  .sync-table td {
    padding: 0.5rem 0.4rem;
  }

  .sync-table th:first-child,
  .sync-table td:first-child {
    padding-right: 0.25rem;
  }
}

/* Admin dashboard (admin/dashboard/show): top-level links as tappable
   cards instead of bare " · "-joined text, and the two sync buttons in a
   wrapping row -- both hold up on a phone-width screen. */
.admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.admin-nav__link {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.5rem 0.9rem;
  color: var(--text);
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
}

.admin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

/* button_to wraps each button in its own <form>; that's the actual flex
   item, so the sizing rule targets the form, not .button. */
.admin-actions form {
  flex: 1 1 auto;
  margin: 0;
}

.admin-actions .button {
  display: block;
  width: 100%;
  text-align: center;
}

.admin-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.admin-list__item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.5rem 0.75rem;
  font-size: 0.9rem;
}

.admin-tabs {
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}

.admin-tabs__tab {
  padding: 0.5rem 0.75rem;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 0.85rem;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
}

.admin-tabs__tab--active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1.5rem;
  font-size: 0.9rem;
}

th,
td {
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

th {
  color: var(--text-dim);
  font-weight: 600;
  font-size: 0.8rem;
}

/* Admin games (admin/games#index): one card per game instead of a wide
   table -- the old table's score/completed/voided form needed horizontal
   scrolling to reach on a phone. */
.game-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* The card shell itself is the one declared up with Home's board -- a second
   .game-card rule down here quietly won on both pages, which is why Home's
   padding looked stuck no matter what that rule said. */

.game-card__matchup {
  font-weight: 600;
}

.game-card__kickoff {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin-bottom: 0.75rem;
}

.game-card__scores {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 0.6rem;
}

.game-card__field {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.8rem;
  color: var(--text-dim);
}

.game-card__flags {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.game-card__flags .button--compact {
  margin-left: auto;
}

.game-card__form .button--compact {
  width: auto;
  padding: 0.35rem 0.75rem;
  font-size: 0.8rem;
}

/* Admin users (admin/users#index): same one-card-per-row treatment as
   .game-card, but the role form is one field so it fits on one line. */
.user-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.user-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
}

.user-card__identity {
  margin-bottom: 0.6rem;
}

.user-card__name {
  font-weight: 600;
  color: var(--text);
}

.user-card__email {
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* Admin teams (admin/teams#index): .user-card's one-card-per-row treatment,
   but with two fields per row, so the form wraps on a phone instead of
   squeezing both into one line. */
.admin-hint {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin-bottom: 1rem;
}

.team-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.team-filters .text-input {
  flex: 1 1 8rem;
  width: auto;
  min-width: 0;
}

.team-filters__check {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--text-dim);
  font-size: 0.85rem;
  white-space: nowrap;
}

.team-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.team-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
}

.team-card__identity {
  margin-bottom: 0.6rem;
}

.team-card__name {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: 600;
  color: var(--text);
}

.team-card__form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

.team-card__form .text-input {
  flex: 1 1 10rem;
  width: auto;
  min-width: 0;
}

/* Wide enough for the "Conference" placeholder to read in full, still
   narrow enough that both fields sit on one line on a phone. */
.team-card__form .text-input--short {
  flex: 0 1 9rem;
}

.team-card__form .button--compact {
  flex: 0 0 auto;
  width: auto;
  padding: 0.35rem 0.75rem;
  font-size: 0.8rem;
}

/* Name, auto-pick and season card are one identity block: no gap above it
   (the page starts where Home's and Week's do), tight inside it, then a
   wider break before Pick history -- 0 / 0.25 / 1 / 1.75rem. */
.profile-name {
  margin: 0 0 0.25rem;
}

.profile-auto-pick {
  margin: 0 0 1rem;
  color: var(--text-dim);
}

/* Season Archives player page -- which past season this is, right under
   the player's name, a pill so it can't be mistaken for plain copy. */
.profile-archive-season {
  margin-bottom: 1rem;
}

.profile-stats {
  margin-bottom: 1.75rem;
  color: var(--text-dim);
}

.profile-history h2 {
  color: var(--text-dim);
}

/* Purely structural -- list semantics only. Its shared/_group_divider
   child owns all the spacing between one period's picks and the next's. */

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

/* The .slot-card inside each <li> already owns padding/margin -- the <li>
   itself is just list semantics. */

/* Settings -- a stack of small cards (2025 app's pattern), one setting (or
   group of related settings) per card, instead of one long bare form. */
.settings-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.setting-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.6rem 0.75rem;
}

.setting-card__label {
  display: block;
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  color: var(--text-dim);
  margin-bottom: 0.25rem;
}

.setting-card__value {
  font-size: 1rem;
}

/* Shared by the Account row (email + Log out) and the Role row (badge +
   Admin link) -- label/value on the left, an action on the right. */
.setting-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.setting-card__hint {
  font-size: 0.8rem;
  color: var(--text-dim);
  margin: 0.15rem 0 0.6rem;
}

/* Notifications card: hint text doubling as a row's left side (next to the
   toggle) instead of sitting on its own block above/below -- no bottom
   margin needed there. */
.setting-card__row .setting-card__hint {
  margin: 0;
}

/* Nickname/auto-pick: field + its own Save button, one row, never wrapping
   to a second line even on a narrow phone -- the field shrinks (min-width:
   0 overrides its own flex-basis auto, which would otherwise refuse to
   shrink below its content) rather than pushing Save down or off-screen. */
.setting-card__inline-form {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 0.5rem;
}

.setting-card__inline-form .text-input {
  flex: 1 1 auto;
  min-width: 0;
}

.setting-card__inline-form .button--compact {
  flex: 0 0 auto;
  width: auto;
  padding: 0.35rem 0.75rem;
  font-size: 0.8rem;
}

.text-input {
  display: block;
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 0.4rem;
  padding: 0.55rem 0.7rem;
  color: var(--text);
  /* iOS Safari auto-zooms on focus for any input under 16px, and never
     zooms back out -- keep this at 1rem to avoid that trap. */
  font-size: 1rem;
}

.text-input:focus {
  outline: none;
  border-color: var(--accent);
}

/* Every button on Settings is one of these inline, sits-next-to-its-field
   ones (Log out next to the account email, Save next to each card's own
   field) -- same colors as .button, just a smaller footprint than the
   solid full-width default. */
.settings-list .button--compact {
  width: auto;
  padding: 0.35rem 0.75rem;
  font-size: 0.8rem;
}

.button--danger {
  background: var(--danger);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f5f6f8;
    --surface: #ffffff;
    --text: #1a1e24;
    --text-dim: #5b6472;
    --border: #dde1e6;
  }
}


/* ---------------------------------------------------------------------
   Leaderboard: one card per player, three columns -- rank | name + record/
   streak | trend + points -- so the rank badge and the trend/points group
   can center vertically against the two-line name column instead of
   pinning to its top line. Rank 1-3 use the same chip badge the pick slots
   use (gold/silver/bronze here).
   ------------------------------------------------------------------ */

/* Below every standings row, not up with the title -- past seasons, one
   tap away, without competing for attention with this season's board. */
.past-seasons {
  margin-top: 3.5rem;
}

/* Season Archives show page -- back arrow + title in one row, same idiom
   as week-header's prev/next, but left-aligned since there's only one
   direction to go (back to the Season page the list lives on). */
.season-archive-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.season-archive-header h1 {
  font-size: 1.25rem;
  margin: 0;
}

.standings-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.standings-card {
  display: grid;
  /* Rank column is a fixed width, not auto -- each card is its own grid
     (standings-list is a flex column of separate <a>s), so an auto track
     sizes to that row's own rank content alone: rank 1-3's chip badge vs.
     4+'s bare number would otherwise size the column differently per row,
     knocking the info column out of alignment underneath it. */
  grid-template-columns: 2.25rem 1fr auto;
  align-items: center;
  column-gap: 0.75rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 0.5rem;
  text-decoration: none;
}

.standings-card__rank-col {
  display: flex;
  align-items: center;
  justify-content: center;
}

.standings-card__rank {
  font-weight: 700;
  font-size: 1.2rem;
  color: var(--text-dim);
}

.standings-card__info {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

/* .value-pill (application.css) carries the look now, matching the same
   tag used on the Week board's cards -- this just keeps it from
   stretching full-width in the flex column and truncates a long name. */
.standings-card__name {
  align-self: flex-start;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.standings-card__meta {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: var(--text-dim);
}

.standings-card__dot {
  opacity: 0.6;
}

.standings-card__totals {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* White for the podium (rank 1-3); everyone else's total is the same
   neutral gray as their record/streak line -- no green/red anywhere on
   this card, the numbers speak for themselves. Same bold sans-serif
   treatment as a pick card's own point value (.slot-card__base-value) --
   monospace here read as a mismatched, unrelated number style. */
.standings-card__points {
  font-weight: 700;
  font-size: 1.2rem;
  color: var(--text);
  white-space: nowrap;
}

.standings-card__points--muted {
  color: var(--text-dim);
}

.standings-trend {
  flex: none;
  display: block;
  width: 3.25rem;
  height: 1.15rem;
}

.standings-trend polyline {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  opacity: 0.85;
}
