/* =============================================================================
   UltiGameMate — app-screen components
   Used by the app shell: sidebar, tab bar, dashboard, picks, scores.
   Loaded last, after tokens.css + style.css. Mirrors design/ugm.pen.
   ============================================================================= */

/* --- shell : sidebar + content (desktop) / stacked + tab bar (mobile) ------ */
.app-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-block-size: 100dvh;
  background: transparent; /* let the body's aurora glow through */
}

@media (min-width: 64rem) {
  .app-shell {
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  }
}

.app-main {
  inline-size: 100%;
  max-inline-size: var(--w-app);
  margin-inline: auto;
  padding: clamp(1.25rem, 0.8rem + 2vw, 2.5rem) var(--pad-inline)
    calc(var(--tabbar-h) + var(--space-2xl));
  display: flex;
  flex-direction: column;
  gap: var(--gap-section);
  font-family: var(--font-body);
  font-size: var(--fz-body);
  line-height: var(--lh-body);
  color: var(--text-body);
}

/* Flex items default to min-width:auto, which refuses to shrink below a wide
   descendant's intrinsic width (e.g. .twrap table's min-inline-size) — that
   pushes .app-main, .app-shell and body wider than the viewport instead of
   letting .twrap's own overflow-x:auto scroll the table locally. Real page-
   level horizontal overflow on mobile is known to break position:fixed bars
   (the tab bar), so every screen-level child must be allowed to shrink. */
.app-main > * {
  min-inline-size: 0;
}

@media (min-width: 64rem) {
  .app-main {
    /* anchor content against the sidebar instead of centring it in a void */
    margin-inline: 0;
    padding-inline: clamp(2rem, 1rem + 3vw, 4rem);
    padding-block-end: var(--space-3xl);
  }
}

/* admin screens hold wide data tables — give them more room than a normal
   app screen, but keep sparse tables from stretching edge to edge */
body[class^='admin'] .app-main {
  max-inline-size: 72rem;
}

/* --- admin form spacing ---------------------------------------------------
   Admin pages build forms out of bare label/input/select/textarea/button
   siblings (no field wrapper), and mix pages with a <form> and pages
   without one. Neither the generic `label + input` rule nor the
   `form :is(...) + :is(...)` rule reaches every case, so a control that is
   directly followed by the *next* field's label (or by an action button)
   gets no margin at all — fields and buttons end up touching. These rules
   give every admin page one consistent rhythm regardless of markup:
   space-xs between a label and its own control, space-lg before the next
   field or action button. */

/* inputs/selects/textareas are inline-block and .cta is inline-flex, so a
   margin alone doesn't reliably start a new line — it just shifts the next
   element within the same shared line box. Force fields onto their own
   line so the rhythm below is guaranteed rather than width-dependent.
   Covers admin AND app pages — profile.liquid uses the same bare
   label/input/button siblings admin forms do (leagues.liquid is the only
   app page that wraps fields in a div instead). */
:where(body[class^='admin'], body[class^='app'])
  :is(input, select, textarea):not([type='checkbox'], [type='radio']) {
  display: block;
}

:where(body[class^='admin'], body[class^='app']) label + :is(input, select, textarea) {
  margin-block-start: var(--space-xs);
}

:where(body[class^='admin'], body[class^='app'])
  :is(input, select, textarea, label)
  + :is(label, .cta, .btn, button) {
  margin-block-start: var(--space-lg);
}

/* The edit-match dialog has six action buttons of very different label
   lengths ("Lock" vs "Fetch data") — flex-wrap with
   justify-content:flex-end (the generic dialog-actions rule) right-justifies
   each wrapped row independently, which reads as ragged/disordered. A grid
   of even-width cells wraps predictably instead. */
body[class^='admin'] .dialog-actions {
  margin-block-start: var(--space-lg);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: var(--gap-sm);
}

/* .link (Cancel) has no button background, so it needs its own text
   centered within its stretched grid cell to match the solid .cta buttons
   either side of it. */
body[class^='admin'] .dialog-actions .link {
  justify-content: center;
}

/* admin matches table: icon markers for stats / status (label in the tooltip
   and screen-reader text), and a round-over-kick-off details cell */
.twrap :is(td.flags, td.when) {
  white-space: nowrap;
}

.flag {
  display: inline-flex;
  vertical-align: middle;
  font-size: 1.25em;
}

.flag + .flag {
  margin-inline-start: var(--gap-xs);
}

.flag--ok {
  color: var(--pos);
}

.flag--live {
  color: var(--neg);
}

.flag--off {
  color: var(--text-muted);
}

.flag--locked {
  color: var(--gold);
}

.when__round {
  display: block;
  color: var(--text-primary);
}

.when__time {
  display: block;
  color: var(--text-muted);
  font-size: var(--fz-xs);
}

/* leagues.liquid wraps each label+control pair in a plain <div> rather than
   leaving bare siblings, so the block-level div already forces a new line
   before whatever follows — it just has no gap. Give a field-wrapper div
   the same space-lg break before the next field or action button that
   bare-sibling forms get from the rule above. */
body[class^='app'] form > div + :is(div, .cta, .btn, button) {
  margin-block-start: var(--space-lg);
}

.app-main :is(h1, h2, h3) {
  margin: 0;
}

/* dense one-line UI rows keep the tighter leading */
.app-main :is(.page-head, .section-header, .stat-row, .round-tabs) {
  line-height: var(--lh-ui);
}

.app-screen {
  display: contents;
}

/* Vertical rhythm for app screens.
   select.liquid wraps content in <div class="app-screen"> (display:contents),
   so its children are direct .app-main flex items and inherit its gap.
   dashboard.liquid / scores.liquid wrap content in a plain <div id="*-screen">
   — give that the same column rhythm explicitly (global resets zero p/heading
   margins, so without this everything collapses together). */
.app-main > [id$='-screen']:not(.app-screen) {
  display: flex;
  flex-direction: column;
  gap: var(--space-lg);
}

/* a section label hugs the block it introduces, with a larger break above it */
.app-main > [id$='-screen']:not(.app-screen) > .section-header {
  margin-block-start: var(--space-sm);
  margin-block-end: calc(var(--space-xs) - var(--space-lg));
}

/* a section-header + its content + optional trailing link/button, hidden as
   one unit until that section's data has loaded (see pages/*.js) so nothing
   pops in piecemeal or leaves a header/button floating above empty space */
.section-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* the header block gets a little extra air below it — applies to app-screen
   pages too (select.liquid) even though display:contents means .page-head
   ends up a direct .app-main flex item rather than this wrapper's */
.app-main > [id$='-screen'] > .page-head {
  margin-block-end: var(--space-2xs);
}

/* select.liquid's <div class="app-screen"> uses display:contents, so its
   children (including #pick-status) become direct .app-main flex items and
   inherit .app-main's much larger inter-section gap (--gap-section) instead
   of the --space-lg rhythm every other app screen uses around its status
   line. Cancel the difference so the gap above and below matches exactly. */
#pick-status {
  margin-block: calc(var(--space-lg) - var(--gap-section));
}

/* leagues/leaderboard/profile still use the older bare-<section> prose
   layout (no flex gap at all), so their status line only had the default
   paragraph margin and font-size — inconsistent with (and mostly smaller
   than) the rhythm above. The prose fallback's `section:not([class]) p`
   rule outranks .page-head__meta's class selector, so font-size/color have
   to be set here too (by id) to actually win. margin-block-start matches
   the 28px gap dashboard/scores get from their flex-gap + page-head margin. */
:is(#leagues-status, #leaderboard-status, #profile-status) {
  margin-block: calc(var(--space-lg) + var(--space-2xs)) var(--space-lg);
  font-size: var(--fz-sm);
  color: var(--text-secondary);
}

/* --- page header --------------------------------------------------------- */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-md);
}

.page-head__title {
  font-family: var(--font-display);
  font-size: var(--fz-2xl);
  font-weight: 700;
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-heading);

  /* white shading to soft grey, clipped to the glyphs */
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.page-head__meta {
  font-size: var(--fz-sm);
  color: var(--text-secondary);
}

.round-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-xs);
  padding: var(--pad-xs) var(--pad-lg) var(--pad-xs) var(--pad-md);
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-pill);
  background:
    linear-gradient(var(--accent-wash), var(--accent-wash)) padding-box,
    linear-gradient(var(--bg-page), var(--bg-page)) padding-box,
    var(--edge-accent) border-box;
  box-shadow: var(--glow-accent);
  color: var(--accent-hi);
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

/* The label is written by page JS after load (#dash-gw / #gw-label), so on
   first paint the pill would be an icon-only stub that then widens. Keep it
   invisible until the label exists, then fade it in at its final width. */
.round-pill {
  transition: opacity var(--dur) var(--ease);
}

.round-pill:has(> span:empty) {
  opacity: 0;
}

/* --- section header (green tick + label) ------------------------------- */
.section-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-block-start: var(--space-xs);
}

/* header without an icon: a short gradient tick leads the label */
.section-header::before {
  content: '';
  inline-size: var(--icon-md);
  block-size: var(--space-3xs);
  flex: none;
  background: var(--grad-accent);
  border-radius: var(--space-3xs);
}

.section-header:has(> .icon)::before {
  content: none;
}

/* header with an icon: the icon sits in a small lit badge. .icon-badge is
   the same badge anywhere else (headings, callouts) — pass it as the icon
   partial's `class`. */
.section-header > .icon,
.icon-badge {
  --icon-duo: 0.32;
  flex: none;
  box-sizing: content-box;
  inline-size: var(--icon-sm);
  block-size: var(--icon-sm);
  padding: 5px;
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-sm);
  background:
    linear-gradient(var(--accent-wash), var(--accent-wash)) padding-box,
    linear-gradient(var(--bg-card), var(--bg-card)) padding-box,
    var(--edge-accent) border-box;
  box-shadow: var(--glow-accent);
  color: var(--accent-hi);
}

/* prose-page headings (leagues) lead with a slightly larger badge */
:is(h1, h2, h3):has(> .icon-badge) {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-sm);
}

:is(h1, h2, h3) > .icon-badge {
  inline-size: var(--icon-md);
  block-size: var(--icon-md);
  padding: 6px;
  border-radius: var(--radius-md);
}

/* a badge leading one line of text: player name (user), your rank (leagues) */
:is(.player-line, .rank-callout) {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

/* a label led by a small icon ("Round" selector on user / scores) */
label.label-icon {
  display: flex;
  align-items: center;
  gap: var(--gap-xs);
}

.label-icon .icon {
  --icon-duo: 0.3;
  font-size: 1.25em;
  color: var(--accent-hi);
}

/* podium ranks in leaderboard tables: gold / silver / bronze medals */
.rank-medal {
  --icon-duo: 0.35;
  display: inline-flex;
  align-items: center;
  gap: var(--gap-2xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.rank-medal .icon {
  font-size: 1.2em;
  filter: drop-shadow(0 0 5px color-mix(in srgb, currentColor 45%, transparent));
}

.rank-medal--1 {
  color: var(--gold);
}

.rank-medal--2 {
  color: var(--silver);
}

.rank-medal--3 {
  color: var(--bronze);
}

/* a hairline runs on from the label and fades out */
.section-header::after {
  content: '';
  flex: 1;
  block-size: var(--border-hairline);
  background: linear-gradient(90deg, var(--line-strong), transparent);
}

.section-header > span,
.section-header__label {
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* picks auto-save state (Saving… / Saved), pushed past the header's hairline */
.section-header > .save-state {
  order: 1;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
}

.section-header > .save-state[data-type='error'] {
  color: var(--neg);
}

/* --- generic stacks --------------------------------------------------- */
.stack-sm {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

.hidden {
  display: none !important;
}

/* --- match card (fixture in Your Picks) ------------------------------ */
.match-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding: var(--pad-card);
  border-radius: var(--radius-card);
}

.match-card--viewable {
  cursor: pointer;
}

.match-card--viewable:is(:hover, :focus-visible) {
  --_fill: var(--bg-elevated);
  --_edge: var(--edge-clickable-hover);
}

.match-card__date {
  text-align: center;
  font-size: var(--fz-xs);
  color: var(--text-secondary);
}

.match-card__row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
}

.match-card__team {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-body);
  font-size: var(--fz-md);
  font-weight: 600;
  color: var(--text-primary);
}

.match-card__team--home {
  justify-content: flex-end;
}

.match-card__team img {
  flex: none;
  inline-size: 18px;
  block-size: 18px;
}

.match-card__score {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-ui);
  font-size: var(--fz-lg);
  font-weight: 700;
  color: var(--text-primary);
}

.match-card__score span {
  color: var(--text-muted);
}

.match-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
}

/* A lone child sits at the end of the row: Remove with no boost pill (so it
   never jumps sideways when a pill appears), or a locked round's pill. */
.match-card__foot > :only-child {
  margin-inline-start: auto;
}

/* Remove is shaped like the boost pill next to it, so the foot row keeps a
   constant height whether or not the "Attach <boost>" pill is present —
   arming a boost then doesn't grow every pick card and shove the page. */
.match-card__remove {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-sm);
  padding: var(--pad-md) var(--pad-lg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, rgb(255 255 255 / 0.04), transparent 70%);
  font: inherit;
  font-size: var(--fz-sm);
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition:
    color var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    background var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

.match-card__remove {
  box-shadow: var(--shadow-raised-sm);
}

.match-card__remove:active {
  box-shadow: var(--shadow-pressed);
}

.match-card__remove:is(:hover, :focus-visible) {
  color: var(--neg);
  border-color: color-mix(in srgb, var(--neg) 70%, transparent);
  background: color-mix(in srgb, var(--neg) 10%, transparent);
}

/* --- pick option (available match row) ----------------------------- */
.pick-option {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  inline-size: 100%;
  padding: var(--pad-card);
  border-radius: var(--radius-card);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

/* Row 1: the checkbox on the left, the kick-off date in the centre, the
   trailing day/time / boost pill on the right. Same 1fr auto 1fr grid as
   Row 2 (.pick-option__row, teams + vs) so the date sits over the vs badge. */
.pick-option__head {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-sm);
  min-block-size: 20px;
}

.pick-option__head > :last-child {
  justify-self: end;
}

.pick-option:is(:hover, :focus-visible) {
  --_fill: var(--bg-elevated);
}

.pick-option:is(:hover, :focus-visible):not(.pick-option--selected, .pick-option--boosted) {
  --_edge: var(--edge-clickable-hover);
}

.pick-option:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.pick-option--selected {
  --_tint: var(--accent-wash);
  --_edge: var(--edge-accent);
  box-shadow:
    var(--_shadow),
    0 12px 32px -20px color-mix(in srgb, var(--accent-hi) 70%, transparent);
}

.pick-option--boosted {
  --_edge: var(--edge-gold);
  box-shadow: var(--_shadow), var(--glow-gold);
}

.pick-option__check {
  flex: none;
  inline-size: 20px;
  block-size: 20px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-xs);
  background: var(--bg-input);
  border: 1.5px solid var(--line-strong);
  box-shadow: var(--shadow-well-input);
  color: var(--accent-ink);
  transition:
    background var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

.pick-option--selected .pick-option__check {
  background: var(--grad-accent);
  border-color: transparent;
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.3),
    0 0 12px -2px color-mix(in srgb, var(--accent-hi) 60%, transparent);
}

.pick-option__check svg {
  inline-size: 12px;
  block-size: 12px;
  stroke-width: 3;
  opacity: 0;
}

.pick-option--selected .pick-option__check svg {
  opacity: 1;
}

/* Row 2: same grid as .match-score-card__row so the vs badge sits in the
   exact centre column that a finished card's score badge does — the two
   card types stay aligned down the list. */
.pick-option__row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
}

.pick-option__team {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-inline-size: 0;
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 600;
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.pick-option__team--home {
  justify-content: flex-end;
  text-align: end;
}

/* Same treatment as .match-score-card__badge (dashboard/scores) so "v" reads
   identically everywhere it appears, instead of plain muted text here. */
.pick-option__vs {
  flex: none;
  padding: var(--pad-2xs) var(--pad-sm);
  border: var(--border-hairline) solid rgb(var(--rim-rgb) / 0.05);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  box-shadow: var(--shadow-well);
  color: var(--text-primary);
  font-family: var(--font-ui);
  font-size: var(--fz-xs);
  font-weight: 700;
}

.pick-option__team img {
  flex: none;
  inline-size: 18px;
  block-size: 18px;
}

.pick-option__date,
.pick-option__day {
  font-size: var(--fz-xs);
  color: var(--text-secondary);
}

/* Same gold-pill language as .points-badge--boost / .boost-pill--active,
   not plain colored text — "a boost is applied" should look the same
   everywhere it's shown. */
.pick-option__boost__pill {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-2xs);
  padding: var(--pad-2xs) var(--pad-md);
  border-radius: var(--radius-pill);
  background: var(--gold-wash);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 30%, transparent);
  font-size: var(--fz-xs);
  font-weight: 700;
  color: var(--gold);
}

.pick-option__boost__pill svg {
  inline-size: 0.9em;
  block-size: 0.9em;
}

/* --- boost pills --------------------------------------------------- */
.boost-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--space-xs);
}

.boost-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--pad-md) var(--pad-lg);
  border-radius: var(--radius-md);
  color: var(--text-body);
  font: inherit;
  font-size: var(--fz-sm);
  font-weight: 600;
  cursor: pointer;
}

.boost-pill svg {
  --icon-duo: 0.35;
  flex: none;
  inline-size: 14px;
  block-size: 14px;
  color: var(--gold);
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--gold) 45%, transparent));
}

.boost-pill:is(:hover, :focus-visible):not(:disabled) {
  --_fill: var(--bg-elevated);
}

.boost-pill:is(:hover, :focus-visible):not(:disabled, .boost-pill--active) {
  --_edge: var(--edge-clickable-hover);
}

.boost-pill:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}

.boost-pill__count {
  margin-inline-start: auto;
  padding: var(--pad-3xs) var(--pad-sm);
  border-radius: var(--radius-pill);
  background: var(--gold-wash);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 25%, transparent);
  color: var(--gold);
  font-size: var(--fz-2xs);
  font-weight: 700;
}

.boost-pill--active {
  --_tint: var(--gold-wash);
  --_edge: var(--edge-gold);
  box-shadow: var(--_shadow), var(--glow-gold);
  color: var(--gold);
}

.boost-pill--active .boost-pill__count {
  background: var(--grad-gold);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.35);
  color: var(--ink-on-bright);
}

/* --- stat tiles -------------------------------------------------- */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
  gap: var(--space-sm);
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  padding: var(--pad-card);
  border-radius: var(--radius-card);
}

/* duotone icon in a small lit badge (js/icons.js statTile) */
.stat-tile__icon {
  --icon-duo: 0.32;
  display: grid;
  place-items: center;
  inline-size: 2rem;
  block-size: 2rem;
  margin-block-end: var(--space-xs);
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-sm);
  background:
    linear-gradient(var(--accent-wash), var(--accent-wash)) padding-box,
    linear-gradient(var(--bg-card), var(--bg-card)) padding-box,
    var(--edge-accent) border-box;
  box-shadow: var(--inset-hi);
  color: var(--accent-hi);
  font-size: var(--icon-md);
}

.stat-tile__value {
  font-family: var(--font-ui);
  font-size: var(--fz-xl);
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

.stat-tile__label {
  font-family: var(--font-body);
  font-size: var(--fz-2xs);
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* --- round tabs ----------------------------------------------- */
.round-tabs {
  display: flex;
  gap: var(--space-xs);
  overflow-x: auto;
  scrollbar-width: none;

  /* the scroll strip clips anything outside its box — pad it out far enough
     for the tabs' raised shadows, and pull it back by the same amount so the
     tabs stay aligned with the content edge */
  padding: var(--space-xs) var(--space-md) var(--space-md) var(--space-xs);
  margin: calc(-1 * var(--space-xs)) calc(-1 * var(--space-md)) calc(-1 * var(--space-md))
    calc(-1 * var(--space-xs));
}

.round-tabs::-webkit-scrollbar {
  display: none;
}

.round-tab {
  flex: none;
  padding: var(--pad-xs) var(--pad-lg);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font: inherit;
  font-size: var(--fz-sm);
  font-weight: 600;
  cursor: pointer;
}

button.round-tab:is(:hover, :focus-visible) {
  --_fill: var(--bg-elevated);
  color: var(--text-primary);
}

.round-tab--static {
  color: var(--text-primary);
}

/* profile Appearance switch: the round-tab segmented control at button size */
.theme-switch .round-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-sm);
  padding: var(--pad-lg) var(--pad-2xl);
  font-size: var(--fz-md);
}

.theme-switch .round-tab .icon {
  inline-size: 1.15em;
  block-size: 1.15em;
}

/* the selected round reads as a pressed-in segment (segmented control) */
.round-tab--active,
.round-tab--active:is(:hover, :focus-visible) {
  --_tint: var(--accent-wash);
  --_edge: var(--edge-accent);
  box-shadow: var(--shadow-pressed);
  color: var(--accent-hi);
}

/* --- match score card (scores list) ------------------------- */
.match-score-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  inline-size: 100%;
  padding: var(--pad-card);
  border-radius: var(--radius-card);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.match-score-card[disabled] {
  cursor: default;
}

.match-score-card:is(:hover, :focus-visible):not([disabled]) {
  --_fill: var(--bg-elevated);
  --_edge: var(--edge-clickable-hover);
}

.match-score-card__row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
}

.match-score-card__team {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 600;
  color: var(--text-primary);
}

.match-score-card__team--home {
  justify-content: flex-end;
}

.match-score-card__team img {
  flex: none;
  inline-size: 18px;
  block-size: 18px;
}

.match-score-card__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-xs);
  padding: var(--pad-2xs) var(--pad-md);
  border: var(--border-hairline) solid rgb(var(--rim-rgb) / 0.05);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken);
  box-shadow: var(--shadow-well);
  font-family: var(--font-ui);
  font-size: var(--fz-md);
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.match-score-card__badge span {
  color: var(--text-muted);
}

.match-score-card__points {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
}

.match-score-card__date {
  text-align: center;
  font-size: var(--fz-xs);
  color: var(--text-secondary);
}

.points-badge {
  /* at badge size a solid glyph reads better than duotone */
  --icon-duo: 1;
  display: inline-flex;
  align-items: center;
  gap: var(--gap-2xs);
  padding: var(--pad-2xs) var(--pad-md);
  border-radius: var(--radius-pill);
  background: var(--pos-wash);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--pos) 24%, transparent);
  color: var(--pos);
  font-size: var(--fz-xs);
  font-weight: 700;
}

.points-badge svg {
  inline-size: 0.9em;
  block-size: 0.9em;
}

.points-badge--muted {
  background: var(--bg-elevated);
  box-shadow: inset 0 0 0 1px rgb(var(--rim-rgb) / 0.06);
  color: var(--text-secondary);
}

/* Marks a standings total that includes provisional points from the round
   currently in progress (leaderboard / leagues / your-rank). */
.live-dot {
  font-size: var(--fz-2xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--accent-hi);
  vertical-align: 0.05em;
  margin-inline-start: var(--gap-2xs);
}

/* A boost that was actually applied to a match — gold, matching every other
   "boost is active" treatment in the app (.boost-pill--active, .boost-tag).
   Distinct from .points-badge--muted, which is for neutral states like
   "used up" in the boosts-remaining list. */
.points-badge--boost {
  background: var(--gold-wash);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 30%, transparent);
  color: var(--gold);
}

/* --- read-only surfaces -------------------------------------------------
   One rule for the whole app: RAISED = CLICKABLE, DENTED = READ-ONLY.
   The raised look (top sheen, lit top edge, drop shadow, hover) is reserved
   for things that respond to a tap. Display-only cards are pressed into the
   page instead (inset neumorphism — tokens.css --dent-fill / --edge-dent /
   --shadow-dent): shadow inside the top edge, a lit bottom lip, no hover,
   default cursor. Applies to:
   - a match card with no score to open (pending pick on dashboard/scores),
   - a pick card that isn't finished yet (Picks: "Your picks"), and the
     Boosts remaining rows on /app/scores/ (same .match-card markup),
   - stat tiles,
   - the round label on /app/scores/ when only one round is scored (a lone
     tab has nothing to switch to, so it renders as a <span>, not a button).
   Controls inside a dent (Remove, Attach boost, icon badges) stay raised,
   which reads naturally: something to press, sitting in a recess. */
.match-score-card[disabled],
.match-card:not(.match-card--viewable),
.stat-tile,
.round-tab--static {
  background:
    linear-gradient(var(--_tint), var(--_tint)) padding-box,
    var(--dent-fill) padding-box,
    var(--edge-dent) border-box;
  box-shadow: var(--shadow-dent);
  cursor: default;
}

/* a boost shown on a locked pick is a label, not a toggle — a dented gold
   tag (the tappable "Attach"/attached pills carry data-boostmatch and stay
   raised) */
span.boost-pill:not([data-boostmatch]) {
  background:
    linear-gradient(var(--_tint), var(--_tint)) padding-box,
    var(--dent-fill) padding-box,
    linear-gradient(180deg, var(--shade), color-mix(in srgb, var(--gold) 35%, transparent))
      border-box;
  box-shadow:
    inset 0 1px 3px var(--shade),
    inset 0 -1px 0 color-mix(in srgb, var(--gold) 12%, transparent);
  cursor: default;
}

/* --- clickable surfaces: raised (outer neumorphism) ----------------------
   The counterpart of the dents above: anything you can tap is extruded from
   the page, lit by the same top-left light (tokens.css --sheen-convex,
   --shadow-raised*), and framed by the same faint edge as read-only
   containers (--edge-clickable; a plain --line border on hover). Hover lifts it higher; pressing
   sinks it into a dent. Set through custom properties inside :where() (zero
   specificity), so state rules (selected / boosted / active tab) keep
   control of fill, tint and edge, and stack any glow on top via
   `box-shadow: var(--_shadow), …`. */
:where(
  .match-score-card:not([disabled]),
  .match-card--viewable,
  .pick-option:not(:disabled),
  .result-card,
  .sidebar__profile
) {
  --_sheen: var(--sheen-convex);
  --_shadow: var(--shadow-raised);
  --_edge: var(--edge-clickable);
}

:where(
  .match-score-card:not([disabled]),
  .match-card--viewable,
  .pick-option:not(:disabled),
  .result-card,
  .sidebar__profile
):is(:hover, :focus-visible) {
  --_shadow: var(--shadow-raised-hover);
}

/* small controls get the tighter extrusion */
:where(button.round-tab, button.boost-pill:not(:disabled), span.boost-pill[data-boostmatch]) {
  --_sheen: var(--sheen-convex);
  --_shadow: var(--shadow-raised-sm);
  --_edge: var(--edge-clickable);
}

:where(button.round-tab, button.boost-pill:not(:disabled), span.boost-pill[data-boostmatch]):is(
  :hover,
  :focus-visible
) {
  --_shadow: var(--shadow-raised);
}

:where(
  .match-score-card:not([disabled]),
  .match-card--viewable,
  .pick-option:not(:disabled),
  .result-card,
  .sidebar__profile,
  button.round-tab,
  button.boost-pill:not(:disabled),
  span.boost-pill[data-boostmatch]
):active {
  --_shadow: var(--shadow-pressed);
}

/* --- score-breakdown dialog --------------------------------
   A raised card (it's interactive) holding two dented, read-only panels:
   the scoreboard and the breakdown list. Filled by page JS via sd-* ids. */
.score-dialog {
  --_edge: var(--edge-hover);
  padding: var(--space-md);
  inline-size: min(94vw, 28rem);
  border-radius: var(--radius-lg);

  /* a soft green glow pooled at the top, over the normal surface layers */
  background:
    radial-gradient(
        120% 55% at 50% 0%,
        color-mix(in srgb, var(--accent-hi) 13%, transparent),
        transparent 70%
      )
      padding-box,
    var(--_surface);
  color: var(--text-body);
  box-shadow: var(--shadow-lg);
}

.score-dialog::backdrop {
  background: rgb(5 5 8 / 0.6);
  backdrop-filter: blur(4px);
}

.score-dialog__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-block-end: var(--space-md);
}

/* cancel the generic `dialog form[method='dialog']` top margin (meant for
   footer close-forms) so the close button lines up with the title */
.score-dialog__head form[method='dialog'] {
  margin-block-start: 0;
}

.score-dialog__title {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  color: var(--text-secondary);
}

/* scoreboard: a dented panel, teams either side of a big score */
.score-dialog__match {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
  margin-block-end: var(--space-md);
  padding: var(--space-lg) var(--space-md);
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-md);
  background:
    var(--dent-fill) padding-box,
    var(--edge-dent) border-box;
  box-shadow: var(--shadow-dent);
}

.score-dialog__team {
  min-inline-size: 0;
  font-family: var(--font-body);
  font-size: var(--fz-md);
  font-weight: 600;
  line-height: var(--lh-snug);
  color: var(--text-primary);
  overflow-wrap: anywhere;
}

.score-dialog__team--home {
  text-align: end;
}

.score-dialog__score {
  font-family: var(--font-ui);
  font-size: var(--fz-2xl);
  font-weight: 700;
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* breakdown list: one dented panel, rows split by faint hairlines */
.score-dialog__rows {
  overflow: hidden; /* keep the full-bleed total band inside the corners */
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-md);
  background:
    var(--dent-fill) padding-box,
    var(--edge-dent) border-box;
  box-shadow: var(--shadow-dent);
}

.score-dialog__rows:empty {
  display: none;
}

.score-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 0.7em var(--space-md);
}

.score-row + .score-row {
  border-block-start: var(--border-hairline) solid rgb(var(--rim-rgb) / 0.05);
}

/* the subtotal closes the itemised list with a firmer rule */
.score-row + .score-row--subtotal {
  border-block-start-color: var(--line-strong);
}

.score-row__label {
  flex: 1;
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  color: var(--text-secondary);
}

.score-row__value {
  font-family: var(--font-ui);
  font-size: var(--fz-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}

.score-row--subtotal .score-row__value,
.score-row--subtotal .score-row__label {
  color: var(--text-primary);
  font-weight: 700;
}

/* boosted total: a green band across the foot of the list */
.score-row--total {
  padding-block: 0.85em;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--accent) 34%, transparent),
    color-mix(in srgb, var(--accent) 10%, transparent)
  );
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent-hi) 30%, transparent);
}

.score-row--total .score-row__label {
  color: var(--accent-hi);
  font-weight: 700;
}

.score-row--total .score-row__value {
  font-size: var(--fz-md);
  color: var(--accent-bright);
  font-weight: 700;
}

.score-dialog__foot {
  margin: var(--space-md) 0 0;
  text-align: center;
  font-size: var(--fz-sm);
}

.score-dialog__foot .icon {
  --icon-duo: 0.3;
  font-size: 1.1em;
}

.boost-tag {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-xs);
  inline-size: fit-content;
  margin: 0 auto var(--space-md);
  padding: var(--pad-xs) var(--pad-lg);
  border-radius: var(--radius-pill);
  background: var(--gold-wash);
  box-shadow:
    inset 0 0 0 1px color-mix(in srgb, var(--gold) 30%, transparent),
    var(--glow-gold);
  color: var(--gold);
  font-size: var(--fz-xs);
  font-weight: 700;
}

.boost-tag svg {
  --icon-duo: 1;
  inline-size: 0.9em;
  block-size: 0.9em;
}

/* .icon-btn lives in style.css (shared: banner dismiss + dialog close) */

.link-muted {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-xs);
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--fz-sm);
  color: var(--text-secondary);
  cursor: pointer;
}

.link-muted:is(:hover, :focus-visible) {
  color: var(--text-primary);
  text-decoration: underline;
}

/* --- Picks: two panes on desktop ----------------------------- */
.pick-layout {
  display: grid;
  gap: var(--gap-section);
}

.pick-layout__main,
.pick-layout__aside {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
  min-inline-size: 0;
}

@media (min-width: 80rem) {
  .pick-layout {
    /* wide fixture list + fixed picks/boost rail */
    grid-template-columns: minmax(0, 1fr) 22rem;
    gap: var(--space-2xl);
    align-items: start;
  }

  .pick-layout__aside {
    /* Available matches — the wide left column */
    grid-column: 1;
    grid-row: 1;
  }

  .pick-layout__main {
    /* Your picks + boosts — sticky right rail */
    grid-column: 2;
    grid-row: 1;
    position: sticky;
    inset-block-start: var(--space-lg);
  }

  /* boosts stack in the narrow rail instead of wrapping their labels */
  .pick-layout__main .boost-grid {
    grid-template-columns: 1fr;
  }
}

/* --- mobile tab bar ----------------------------------------
   Scrolls sideways instead of squeezing: every item is a fixed fifth of the
   bar, so five fill it exactly and a sixth (the Admin tab auth.js reveals
   after load, or the extra "App" tab on admin pages) sits off to the right
   to scroll to — the other tabs never resize or jump. The active tab is
   scrolled into view on load (base.liquid). */
.tabbar {
  position: fixed;
  inset-block-end: var(--space-sm);
  inset-inline: var(--space-sm);
  z-index: 20;
  display: flex;
  gap: var(--space-3xs);
  padding: var(--space-2xs);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--space-2xs);
  scrollbar-width: none;
  border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--bg-elevated) 88%, transparent);
  backdrop-filter: blur(16px) saturate(1.3);

  /* the rim is an inset ring, not a pseudo-element: the bar is a scroll
     container, and an absolutely positioned child would scroll away */
  box-shadow:
    var(--shadow-lg),
    inset 0 0 0 1px rgb(var(--rim-rgb) / 0.07),
    var(--inset-hi);
}

.tabbar::-webkit-scrollbar {
  display: none;
}

@media (min-width: 64rem) {
  .tabbar {
    display: none;
  }
}

.tabbar__item {
  flex: 0 0 calc((100% - 4 * var(--space-3xs)) / 5);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3xs);
  padding: var(--pad-sm) 0;
  scroll-snap-align: start;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: var(--fz-2xs);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  transition:
    color var(--dur) var(--ease),
    background var(--dur) var(--ease);
}

.tabbar__item svg {
  inline-size: var(--icon-md);
  block-size: var(--icon-md);
}

.tabbar__item:is(:hover, :focus-visible):not(.tabbar__item--active) {
  color: var(--text-primary);
}

/* quiet active state: a soft green tint, no fill or glow */
.tabbar__item--active {
  --icon-duo: 0.35;
  background: var(--accent-wash);
  color: var(--accent-hi);
}

/* --- desktop sidebar ------------------------------------- */
.sidebar {
  display: none;
}

@media (min-width: 64rem) {
  .sidebar {
    position: sticky;
    inset-block-start: 0;
    block-size: 100dvh;
    display: flex;
    flex-direction: column;
    gap: var(--space-xl);
    padding: var(--space-xl) var(--space-md) var(--space-md);

    /* a hint of green at the top, settling into the sunken tone */
    background: linear-gradient(
      180deg,
      color-mix(in srgb, var(--accent) 7%, var(--bg-sunken)),
      var(--bg-sunken) 45%
    );

    /* vertical hairline, lit at the top and fading out downwards */
    border-inline-end: var(--border-hairline) solid var(--line);
    border-image: linear-gradient(
        180deg,
        rgb(var(--rim-rgb) / 0.1),
        rgb(var(--rim-rgb) / 0.04) 50%,
        rgb(var(--rim-rgb) / 0.01)
      )
      1;
  }
}

.sidebar__logo {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding-inline: var(--pad-lg);
  text-decoration: none;
  font-family: var(--font-display);
  font-size: var(--fz-md);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  color: var(--text-primary);
}

.sidebar__logo :is(svg, img) {
  inline-size: 1.6rem;
  block-size: 1.6rem;
  border-radius: var(--radius-xs);
  color: var(--accent-hi);
  box-shadow:
    0 0 0 1px rgb(var(--rim-rgb) / 0.08),
    0 4px 16px -4px color-mix(in srgb, var(--accent-hi) 55%, transparent);
}

.sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.sidebar__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--pad-md) var(--pad-lg);
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 600;
  color: var(--text-secondary);
  transition:
    color var(--dur) var(--ease),
    background var(--dur) var(--ease);
}

.sidebar__item svg {
  inline-size: var(--icon-md);
  block-size: var(--icon-md);
  transition: filter var(--dur) var(--ease);
}

.sidebar__item:is(:hover, :focus-visible) {
  background: rgb(var(--rim-rgb) / 0.04);
  color: var(--text-primary);
}

.sidebar__item--active,
.sidebar__item--active:is(:hover, :focus-visible) {
  --icon-duo: 0.4;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--accent) 30%, transparent),
    color-mix(in srgb, var(--accent) 6%, transparent)
  );
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-hi) 16%, transparent);
  color: var(--accent-hi);
}

.sidebar__item--active svg {
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--accent-hi) 50%, transparent));
}

/* glowing indicator bar pinned to the sidebar's outer edge */
.sidebar__item--active::before {
  content: '';
  position: absolute;
  inset-block: 22%;
  inset-inline-start: calc(-1 * var(--space-md));
  inline-size: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--grad-accent);
  box-shadow: 0 0 12px color-mix(in srgb, var(--accent-hi) 70%, transparent);
}

.sidebar__spacer {
  flex: 1;
}

.sidebar__profile {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm) var(--space-xs) var(--space-xs);
  border-radius: var(--radius-md);
  text-decoration: none;
  font-size: var(--fz-sm);
  color: var(--text-body);
}

.sidebar__profile:is(:hover, :focus-visible) {
  --_fill: var(--bg-elevated);
  --_edge: var(--edge-clickable-hover);
  color: var(--text-primary);
}

.sidebar__profile--active,
.sidebar__profile--active:is(:hover, :focus-visible) {
  --_tint: var(--accent-wash);
  --_edge: var(--edge-accent);
  color: var(--accent-hi);
}

.sidebar__avatar {
  flex: none;
  inline-size: 30px;
  block-size: 30px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, rgb(255 255 255 / 0.25), transparent 55%), var(--grad-accent);

  /* ring: a gap in the card colour, then a faint mint halo */
  box-shadow:
    0 0 0 2px var(--bg-card),
    0 0 0 3px color-mix(in srgb, var(--accent-hi) 45%, transparent),
    0 4px 12px -2px color-mix(in srgb, var(--accent-hi) 45%, transparent);
  color: var(--accent-ink);
  font-family: var(--font-ui);
  font-weight: 700;
}

/* --- club autosuggest (src/js/clubCombobox.js) ---------------------------
   The wrapper is the positioning context for the popup; it sits inside a
   .column, so the input keeps the shared form styling untouched. */
.combobox {
  position: relative;
  display: flex;
  flex-direction: column;
  min-inline-size: 0;
}

.combobox__list {
  position: absolute;
  inset-block-start: calc(100% + var(--space-3xs));
  inset-inline: 0;
  z-index: 30;
  max-inline-size: 30rem;
  max-block-size: 17rem;
  overflow-y: auto;
  margin: 0;
  padding: var(--space-3xs);
  list-style: none;
  --_fill: var(--bg-elevated);
  --_edge: var(--edge-hover);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.combobox__option {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--pad-sm) var(--pad-md);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--fz-sm);
  color: var(--text-primary);
}

.combobox__option[aria-selected='true'],
.combobox__option:hover {
  background: linear-gradient(180deg, rgb(255 255 255 / 0.2), transparent 55%), var(--grad-accent);
  color: var(--accent-ink);
}

.combobox__crest {
  flex: none;
  object-fit: contain;
}

.combobox__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.combobox__short {
  margin-inline-start: auto;
  padding-inline-start: var(--space-xs);
  font-size: var(--fz-xs);
  color: var(--text-muted);
}

.combobox__option[aria-selected='true'] .combobox__short,
.combobox__option:hover .combobox__short {
  color: inherit;
  opacity: 0.75;
}
