/* BCC2 base styles. Mobile-first. */

:root {
    --fg: #faf6db;         /* cream body text */
    --muted: #b0aa95;      /* dimmed cream — secondary text, borders, alias/inactive bg */
    --accent-dark: #881f05;/* burnt red — buttons, hot accents, dark fills */
    --panel: #55595c;      /* main charcoal — primary panel bg */
    --panel-2: #45484b;    /* darker charcoal — body bg, zebra rows, nested panels, inputs */
    --green:        #8fd17a;  /* fresh green — In RSVP, positive % / cash deltas */
    --green-dark:   #4a7c3a;  /* darker green — status-complete bg, most-improved bg, tri "yes" */
    --badge-bubble: #7a4a0f;  /* muted amber — bubble-boy badge bg */
    --badge-consolation: #6a6f73; /* neutral gray — consolation badge bg */
    --col-shadow: 0 0 6px rgba(0, 0, 0, 0.4);  /* drop shadow for matrix sticky cols */
}

* { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--panel-2);
    color: var(--fg);
    font: 16px/1.45 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
    /* Clip any horizontal overflow from full-bleed bands (.embedded-game
       uses 100vw, which on most browsers includes the vertical scrollbar
       and would otherwise spawn a phantom horizontal scrollbar). `clip`
       beats `hidden` because it doesn't create a scroll container, so
       sticky positioning inside the page still works. */
    overflow-x: clip;
}

a { color: var(--fg); text-decoration: none; }
a:hover { text-decoration: underline; }

/* All buttons read uppercase for consistency. Single-glyph buttons
   (× rm, etc.) are unaffected; this applies via text-transform so
   the source case stays normal-cased and readable. */
button { text-transform: uppercase; }
button.rm { text-transform: none; }

header.site, footer.site {
    padding: 8px 20px;
    border-bottom: 1px solid var(--muted);
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
}
header.site {
    position: sticky;
    top: 0;
    z-index: 50;
    background: var(--panel-2);
}
footer.site {
    border-bottom: 0;
    border-top: 1px solid var(--muted);
    color: var(--muted);
    justify-content: center;
}

.brand {
    display: inline-flex;
    align-items: center;
    background: var(--fg);            /* cream — pops the dark logo on charcoal */
    padding: 6px 12px;
    border-radius: 8px;
}
.brand img {
    height: 36px;
    width: auto;
}

.crumbs {
    color: var(--muted);
    font-size: 18px;
    font-weight: 500;
    line-height: 1.2;
}
.crumbs .sep { margin: 0 8px; opacity: 0.5; }
.crumbs a { color: var(--muted); }
.crumbs a:hover { color: var(--fg); }
.crumbs .current { color: var(--fg); font-weight: 600; }
.crumbs .current.champ { color: var(--fg); font-style: italic; font-weight: 700; }

/* Header search — pinned to the right end of the header. On wide screens
   the input is always visible. On narrow screens it collapses to a
   magnifying-glass button that expands the input on tap. */
.search {
    position: relative;
    margin-left: auto;    /* push to the right edge of the flex header */
    display: flex;
    align-items: center;
}
.search input[type=search] {
    width: 260px;
    padding: 8px 12px;
    background: var(--panel-2);
    color: var(--fg);
    border: 1px solid var(--muted);
    border-radius: 8px;
    font: inherit;
    outline: none;
}
.search input[type=search]:focus { border-color: var(--accent-dark); }
.search-toggle {
    display: none;        /* hidden on desktop; shown on narrow */
    background: var(--panel-2);
    color: var(--fg);
    border: 1px solid var(--muted);
    border-radius: 8px;
    padding: 6px 8px;
    cursor: pointer;
    line-height: 0;
}
.search-toggle:hover { border-color: var(--accent-dark); }
.search .results {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    width: 260px;
    max-height: 60vh;
    overflow-y: auto;
    background: var(--panel);
    border: 1px solid var(--muted);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.35);
    z-index: 60;
}
.search .results.hidden { display: none; }
.search .hit {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 8px 12px;
    color: var(--fg);
    border-bottom: 1px solid var(--muted);
}
.search .hit:last-child { border-bottom: 0; }
.search .hit:hover { background: var(--panel-2); text-decoration: none; }
.search .hit .kind {
    font-size: 10px;
    text-transform: uppercase;
    color: var(--muted);
    min-width: 50px;
}
.search .hit .name { color: var(--fg); }

/* Header account widget — sits to the right of the search box. */
.account {
    display: flex;
    align-items: center;
    gap: 6px;
}
/* No card chrome on .account-link — the inner .player-link calling
   card already provides its own pill styling. */
.account-link:hover { text-decoration: none; }
.account-logout button {
    display: inline-flex;
    align-items: center;
    background: var(--panel-2);
    color: var(--muted);
    border: 1px solid var(--muted);
    border-radius: 6px;
    padding: 5px 8px;
    font: inherit;
    cursor: pointer;
    line-height: 0;
}
.account-logout button:hover { color: var(--accent-dark); border-color: var(--accent-dark); }
.account-signin {
    display: inline-flex;
    align-items: center;
    color: var(--fg);
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 6px;
    padding: 5px 8px;
    line-height: 0;
}
.account-signin:hover { border-color: var(--accent-dark); text-decoration: none; }

@media (max-width: 700px) {
    /* Tighter header: smaller gaps + padding so brand, search, and
       account fit together on one row without awkward wrapping. */
    .site {
        gap: 10px;
        padding: 6px 14px;
    }
    .brand { padding: 4px 10px; }
    .brand img { height: 28px; }

    /* Crumbs always go to their own row below — they can be long
       enough to push the right-side widgets onto a third line. */
    .crumbs {
        order: 99;
        flex: 1 1 100%;
        font-size: 16px;
    }
    .crumbs .sep { margin: 0 5px; }

    .account-logout button { padding: 4px 9px; }

    /* Search: input collapses to a magnifying-glass button; tap to
       expand the input. While open, it takes the whole header so the
       brand/crumbs/account widgets get out of the way. */
    .search-toggle { display: inline-flex; align-items: center; }
    .search input[type=search] { display: none; }
    .search.open { flex: 1 1 100%; }
    .search.open .search-toggle { display: none; }
    .search.open input[type=search] { display: block; width: 100%; }
    .search.open .results { left: 0; right: 0; width: auto; }
    .site:has(.search.open) > :not(.search) { display: none; }
}

main {
    /* Full viewport width, vertical padding only. Each top-level child
       (panel, details, embedded-game) handles its own horizontal
       padding so its background can run viewport edge-to-edge while
       its content stays in a centered ~1000px column. */
    padding: 20px 0 40px;
}
/* Column-centering padding applied to direct children of main:
   enough horizontal padding to push content into a centered ~1000px
   column, with a 12px gutter on narrow screens. */
main > * {
    padding-left:  max(12px, calc((100% - 1000px) / 2));
    padding-right: max(12px, calc((100% - 1000px) / 2));
}

h1 { margin: 0 0 16px; font-size: 28px; color: var(--fg); }

/* Championship games get the red + italic treatment wherever a game
   name appears (h1, list links, table links). */
.champ, .champ .game-name {
    color: var(--fg);
    font-style: italic;
    font-weight: 700;
}
.champ:hover, .champ:hover .game-name { color: var(--fg); }
h2 { margin: 0 0 12px; font-size: 18px; color: var(--muted); text-transform: uppercase; }

.panel {
    background: var(--panel);
    padding-top: 14px;
    padding-bottom: 14px;
    /* Horizontal padding inherited from the column-centering rule on
       :where(main, .embedded-game) > * — bg runs full width, content
       sits in the centered ~1000px column. No margin-bottom — adjacent
       panels are distinguished by alternating bg colors instead of
       body-color gaps (rule below). */
}
/* Alternate .panel + .admin-edit bg colors as a single
   sequence so adjacent stacked elements always read distinct without
   needing vertical gap. Counted among main's children matching
   either selector. */
main > :nth-child(odd of .panel, .admin-edit) {
    background: var(--panel);
}
main > :nth-child(even of .panel, .admin-edit) {
    background: var(--panel-2);
}

/* Per-game leaderboard panels on the season page — collapsed by default,
   the summary row styled to match the .panel h2 look. */
.game-collapse > summary {
    cursor: pointer;
    font-size: 18px;
    color: var(--muted);
    text-transform: uppercase;
    list-style: none;
}
.game-collapse > summary::-webkit-details-marker { display: none; }
.game-collapse > summary::before {
    content: '▸';
    display: inline-block;
    width: 1em;
    color: var(--muted);
    transition: transform 0.15s;
}
.game-collapse[open] > summary::before { transform: rotate(90deg); }
.game-collapse[open] > summary { margin-bottom: 12px; }

.meta {
    display: flex;
    gap: 18px;
    flex-wrap: wrap;
    color: var(--muted);
    font-size: 14px;
}
/* Spacing below .meta only matters when something follows it (e.g. on
   the game page). Inside a .panel where it's the only child, the panel's
   own padding handles spacing — adding margin-bottom here would make the
   bottom gap larger than the top. */
/* Only when .meta is rendered standalone (e.g., inside .player-header)
   does it need its own margin-bottom — when it IS a top-level panel
   (.panel.meta on the game page), the alternating-stack rhythm
   handles spacing and an extra margin would break the flush layout. */
.meta:not(.panel):not(:last-child) { margin-bottom: 14px; }
.meta strong { color: var(--fg); font-weight: 600; }

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

code {
    background: var(--panel-2);
    padding: 2px 5px;
    border-radius: 3px;
    font-size: 14px;
    color: var(--fg);
}

.badge {
    display: inline-block;
    background: var(--accent-dark);
    color: white;
    font-size: 11px;
    font-weight: 700;
    padding: 3px 7px;
    border-radius: 4px;
    text-transform: uppercase;
    vertical-align: middle;
    margin-left: 6px;
}
.badge.small { font-size: 10px; padding: 2px 5px; }
.badge.alias { background: var(--muted); }

.status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
}
.status-scheduled { background: var(--panel-2); color: var(--muted); }
.status-live      { background: var(--fg); color: var(--accent-dark); }
.status-complete  { background: var(--green-dark); color: white; }

.pc.up { color: var(--green); font-weight: 600; margin-left: 4px; }
.pc.dn { color: var(--accent-dark); font-weight: 600; margin-left: 4px; }

/* Season pot summary panel — sits above the championship/leaderboard. */
.pot-summary .pot-headline {
    font-size: 22px;
    font-weight: 600;
    color: var(--fg);
    margin: 4px 0 10px;
}
.pot-summary .pot-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
}
.pot-summary .pot-tile {
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 6px;
    padding: 8px 12px;
}
.pot-summary .pot-tile .asset {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
}
.pot-summary .pot-tile .qty   { font-size: 15px; }
.pot-summary .pot-tile .value { font-size: 15px; font-weight: 600; margin-top: 2px; }

.pot-adjustments { margin-top: 14px; }
.pot-adjustments h3 {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 6px;
}
.pot-adjustments .mini-table { width: auto; font-size: 14px; }
.pot-adjustments .mini-table td { padding: 3px 10px 3px 0; }
.pot-adjustments .mini-table td.num { font-weight: 600; }
.pot-adjustments .mini-table td.num.up { color: var(--green); }
.pot-adjustments .mini-table td.num.dn { color: var(--accent-dark); }
.pot-adjustments .mini-table td.rm-cell form { display: inline; }
.pot-adjustments .add-adjustment {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-top: 8px;
}
.pot-adjustments .add-adjustment input[type=text]   { flex: 1 1 220px; }
.pot-adjustments .add-adjustment input[type=number] { width: 120px; }
.pot-adjustments .add-adjustment input {
    font: inherit;
    padding: 6px 8px;
    border: 1px solid var(--muted);
    border-radius: 5px;
    background: var(--panel-2);
    color: var(--fg);
}
.pot-adjustments .add-adjustment button {
    font: inherit;
    padding: 7px 14px;
    background: var(--accent-dark);
    color: white;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
}

/* Game-attached pot adjustment: tucked under the "Invested:" line. */
.invest-adj {
    font-size: 13px;
    color: var(--muted);
    margin-left: 14px;
}
.invest-adj .amt         { font-weight: 600; margin-left: 4px; }
.invest-adj .amt.up      { color: var(--green); }
.invest-adj .amt.dn      { color: var(--accent-dark); }

/* Admin knockouts panel: mini-table + add form inline like pot adjustments. */
.ko-list tr.self-keep td em { font-style: italic; color: var(--muted); }
.add-knockout {
    display: flex;
    gap: 8px;
    align-items: center;
    margin-top: 8px;
    flex-wrap: wrap;
}
.add-knockout select,
.add-knockout input {
    font: inherit;
    padding: 6px 8px;
    border: 1px solid var(--muted);
    border-radius: 5px;
    background: var(--panel-2);
    color: var(--fg);
}
.add-knockout input[type=number] { width: 160px; }
.add-knockout button {
    font: inherit;
    padding: 7px 14px;
    background: var(--accent-dark);
    color: white;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
}

/* Free entries — asterisk + footnote on the results table, admin-only
   checkbox column for toggling. */
.free-mark       { color: var(--accent-dark); margin-left: 1px; }
.improvement-badge {
    margin-left: 8px;
    padding: 1px 6px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    color: white;
    border-radius: 4px;
    white-space: nowrap;
}
.improvement-badge.most-improved  { background: var(--green-dark); }   /* green */
.improvement-badge.least-improved { background: var(--accent-dark); }
.improvement-badge.bubble-boy     { background: var(--badge-bubble); }
.improvement-badge.consolation    { background: var(--badge-consolation); }
.free-footnote   { color: var(--muted); font-size: 12px; margin: 6px 0 0; }
.free-col        { text-align: center; }
.free-col input  { margin: 0; cursor: pointer; }
.free-save       { margin-top: 8px; }

/* A pot-split row set to "remainder" — the value box is unused so grey it out. */
.pot-split-row.remainder input[type=number] { opacity: 0.45; }

/* Admin inline edit panels */
.admin-edit {
    /* Bg comes from the alternating-stack rule on :where(main,
       .embedded-game) > :nth-child(... of .panel, .admin-edit).
       Horizontal padding from the column-centering rule. */
    padding-top: 8px;
    padding-bottom: 8px;
}
.admin-edit > summary {
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
    padding: 4px 0;
}
.admin-edit[open] > summary { margin-bottom: 10px; }
.admin-edit form {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 10px 14px;
    padding-bottom: 10px;
}
.admin-edit label {
    display: flex;
    flex-direction: column;
    gap: 3px;
    font-size: 12px;
    text-transform: uppercase;
    color: var(--muted);
}
/* Checkbox labels read "[ ] Text", not stacked. */
.admin-edit label:has(> input[type=checkbox]) {
    flex-direction: row;
    align-items: center;
    gap: 6px;
}
.admin-edit label.wide { grid-column: 1 / -1; }
/* Any direct child of an admin-edit form marked .wide spans every column.
   Without this, fieldsets / nested details that live next to single-column
   inputs end up overlapping awkwardly inside auto-fit grid cells. */
.admin-edit > form > .wide { grid-column: 1 / -1; }
.admin-edit input, .admin-edit textarea, .admin-edit select {
    font: inherit;
    padding: 6px 8px;
    border: 1px solid var(--muted);
    border-radius: 5px;
    background: var(--panel-2);
    color: var(--fg);
    text-transform: none;
}
.admin-edit .actions {
    grid-column: 1 / -1;
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    padding-top: 4px;
}
.admin-edit button {
    font: inherit;
    padding: 7px 16px;
    background: var(--accent-dark);
    color: white;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
}
.admin-edit button:hover { filter: brightness(1.1); }

.admin-edit .hint {
    font-size: 11px;
    font-weight: 400;
    text-transform: none;
    color: var(--muted);
    margin-top: 2px;
}
.admin-edit .hint code {
    font-size: 11px;
    padding: 1px 4px;
}
.admin-edit .hint.wide { grid-column: 1 / -1; padding-top: 2px; }

/* Season-level "Default game settings" block — mirrors the structure of the
   game-level override, but nested under the season form. */
.admin-edit fieldset.defaults-block {
    grid-column: 1 / -1;
    border: 1px solid var(--muted);
    border-radius: 6px;
    padding: 6px 12px 10px;
    margin: 4px 0 0;
    background: var(--panel-2);
}
.admin-edit fieldset.defaults-block legend {
    padding: 0 6px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
}
.admin-edit fieldset.defaults-block .defaults-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 8px 14px;
}
/* An inherited value (null on the game, falling back to season) reads
   dimmer than an explicit override — so the admin can see at a glance
   which fields this game has chosen to override. */
.admin-edit fieldset.defaults-block label.field.inherited {
    opacity: 0.55;
}
.admin-edit fieldset.defaults-block label.field.inherited select,
.admin-edit fieldset.defaults-block label.field.inherited input {
    font-style: italic;
}

.admin-edit fieldset.payout-schedule {
    grid-column: 1 / -1;
    border: 1px solid var(--muted);
    border-radius: 6px;
    padding: 8px 12px 10px;
    margin: 4px 0 0;
    background: var(--panel-2);
}
.admin-edit fieldset.payout-schedule legend {
    padding: 0 6px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
}
.admin-edit fieldset.payout-schedule label.inline {
    flex-direction: row;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    margin: 4px 0 6px;
}
.admin-edit fieldset.payout-schedule label.inline input { width: 80px; }
.admin-edit table.mini-table {
    width: 100%;
    border-collapse: collapse;
    margin: 4px 0;
}
.admin-edit table.mini-table th,
.admin-edit table.mini-table td {
    padding: 3px 4px;
    text-align: left;
    border: 0;
}
.admin-edit table.mini-table th {
    font-size: 11px;
    text-transform: uppercase;
    color: var(--muted);
}
.admin-edit table.mini-table input,
.admin-edit table.mini-table select {
    width: 100%;
}
.admin-edit table.mini-table td:last-child  { width: 28px; text-align: center; }
.admin-edit table.mini-table th.auto,
.admin-edit table.mini-table td.auto { background: var(--panel-2); text-align: center; }
.admin-edit table.mini-table td.first-cell { font-weight: 600; color: var(--accent-dark); }
.admin-edit table.mini-table th.auto small { font-weight: 400; color: var(--muted); font-size: 10px; }

/* Payout grid: let each % input breathe, and scroll the table when the
   fieldset's own width is too narrow to fit every column. */
.admin-edit fieldset.payout-schedule { overflow-x: auto; }
.admin-edit table.payout-grid input[type=number] {
    text-align: right;
    min-width: 58px;
}
/* Hide the number-input spinners inside the payout schedule — admin always
   types the numbers in, so the up/down arrows just waste horizontal space.
   The `appearance: textfield` covers Firefox; the WebKit-specific spin-
   button pseudo-elements need their own `appearance: none` to vanish. */
.admin-edit fieldset.payout-schedule input[type=number] {
    appearance: textfield;
}
.admin-edit fieldset.payout-schedule input[type=number]::-webkit-inner-spin-button,
.admin-edit fieldset.payout-schedule input[type=number]::-webkit-outer-spin-button {
    appearance: none;
    margin: 0;
}
.admin-edit button.rm, .admin-edit button.add-row {
    font: inherit;
    padding: 3px 8px;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--muted);
    border-radius: 4px;
    cursor: pointer;
}
.admin-edit button.rm { padding: 1px 7px; font-size: 14px; line-height: 1; }
.admin-edit button.add-row:hover { color: var(--accent-dark); border-color: var(--accent-dark); }

.admin-edit .attend-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 4px 14px;
    padding: 6px 0;
}
.admin-edit label.attend {
    flex-direction: row;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    text-transform: none;
    color: var(--fg);
}
.admin-edit label.attend small { color: var(--muted); margin-left: auto; font-size: 11px; }

.hidden { display: none !important; }

/* CSS-driven reveals — replaces JS class toggles. The container shows
   only when its sibling control is in the relevant state. */
#override-payout-grid                                         { display: none; }
form:has(#override-payout:checked)        #override-payout-grid { display: block; }
#new-location-fields                                          { display: none; }
form:has(#location-choice option[value="new"]:checked) #new-location-fields { display: block; }

/* Lists */
ul.seasons, ul.games {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    gap: 8px;
}
ul.seasons li a, ul.games li a {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 14px;
    padding: 12px 16px;
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 8px;
    color: var(--fg);
}
ul.seasons li a:hover, ul.games li a:hover {
    border-color: var(--accent-dark);
    text-decoration: none;
}
.season-name, .game-name { font-weight: 600; }
.season-range, .game-when { color: var(--muted); font-size: 13px; font-variant-numeric: tabular-nums; }

/* Tables */
.leaderboard, .results {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}
.leaderboard th, .results th,
.leaderboard td, .results td {
    padding: 4px 10px;
    text-align: left;
}
.leaderboard th, .results th {
    font-size: 12px;
    text-transform: uppercase;
    color: var(--muted);
    font-weight: 600;
}
table .num { text-align: right; }
/* Alternate row backgrounds give visual separation without per-row
   borders or extra vertical padding. The lighten-by-alpha works on
   any panel bg, so the same rule applies regardless of which shade
   the surrounding panel happens to have. */
.leaderboard tbody tr:nth-child(even),
.results     tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.05); }
.leaderboard tbody tr:hover,
.results     tbody tr:hover           { background: rgba(255, 255, 255, 0.10); }
table tr.alias td { color: var(--muted); font-style: italic; }

/* All-time leaderboard: cut at top 10 publicly; admins see the long tail
   below a heavier divider, dimmed so it reads as secondary. */
tr.leaderboard-divider td {
    padding: 0;
    border-bottom: 3px double var(--accent-dark);
}
tr.below-cut td { color: var(--muted); }

/* Payouts preview block — label on top, pair row below. The pair row
   never wraps; instead, the rank/cash text and inter-pair gap scale
   with viewport width via clamp() so a long row of payouts squishes
   to fit instead of breaking onto a second line. */
.payouts-preview {
    margin: 0 0 16px;
    padding: 10px 14px;
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 8px;
    overflow: hidden;
}
.payouts-preview .label {
    display: block;
    margin-bottom: 6px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--muted);
}
.payouts-preview .pairs {
    display: flex;
    flex-wrap: nowrap;
    align-items: baseline;
    gap: clamp(6px, 2.5vw, 18px);
    min-width: 0;
}
.payouts-preview .pair {
    display: inline-flex;
    align-items: baseline;
    gap: clamp(3px, 1vw, 6px);
    flex: 0 0 auto;
    white-space: nowrap;
}
.payouts-preview .rank {
    font-size: clamp(9px, 2.2vw, 11px);
    text-transform: uppercase;
    color: var(--muted);
}
.payouts-preview .cash {
    font-size: clamp(14px, 4.5vw, 20px);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--fg);
}

/* RSVP status icons in the season-leaderboard cells of upcoming games.
   The clock is an inline SVG (using currentColor) instead of an emoji
   so the color rule actually takes effect; the rest are plain text
   glyphs that respect color out of the box. */
.rsvp-icon         { font-weight: 700; line-height: 1; }
.rsvp-icon svg     { vertical-align: -2px; }
.rsvp-icon-in      { color: var(--green-dark); }
.rsvp-icon-late    { color: var(--green); }
.rsvp-icon-out     { color: var(--accent-dark); }
.rsvp-icon-unknown { color: var(--muted); }

/* Player avatars — circular images with a textured grey placeholder
   when the player hasn't uploaded one yet. `display: block` avoids the
   <img>-on-baseline phantom gap below image avatars; the placeholder
   uses inline-flex (further down) for centered initial rendering and
   doesn't suffer from this. */
.avatar {
    display: block;
    border-radius: 50%;
    overflow: hidden;
    flex: 0 0 auto;
    object-fit: cover;
    background: var(--panel-2);
}
.avatar-sm { width: 22px; height: 22px; }
.avatar-md { width: 36px; height: 36px; }
.avatar-lg { width: 80px; height: 80px; }
.avatar-placeholder {
    /* Per-player hue is set inline via PHP. Layout here just centers
       the initial inside the circle for whichever size class wins.
       Explicit border-radius so a placeholder is always a circle, even
       if some other selector tries to override the .avatar default.
       container-type: inline-size lets the inner .avatar-init scale
       its font-size off the placeholder's own width via cqi units. */
    color: var(--accent-dark);
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    text-transform: uppercase;
    line-height: 1;
    font-family: system-ui, sans-serif;
    container-type: inline-size;
}
.avatar-init {
    font-size: 38cqi;
    line-height: 1;
    /* The em-box is taller than the cap-height (it reserves space for
       ascenders + descenders we never render), so flex-centered uppercase
       reads visually high. Nudge down a hair so the cap optically centers. */
    transform: translateY(0.06em);
}

/* Inline player rows: avatar + name on a baseline. Used everywhere
   players are listed (RSVP buckets, leaderboards, etc.). The name is
   rendered as a "calling card" — cream pill with burnt-red text — so
   it reads as a tap target without the coral accent. */
.player-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    text-decoration: none;
    /* Center the inline-flex in its line instead of sitting on the
       baseline — otherwise an img-only first child puts the baseline
       at the bottom of the box and the surrounding line reserves
       descender space below it (taller table rows for img-avatar
       players vs placeholder-avatar players). */
    vertical-align: middle;
}
/* Avatar + name pill inside .player-link share the same em-based
   formula for their height, so they always match regardless of the
   surrounding font-size. Pill height = line-height * 1em + 2 * pad-y;
   avatar = same. Width on the avatar mirrors the height to keep it
   round. Everything scales with the inherited font-size. */
.player-link > span:not(.avatar) {
    background: var(--fg);
    color: var(--accent-dark);
    padding: 0.25em 0.75em;          /* pad-y=0.25em → contributes 0.5em to total height */
    line-height: 1.45;
    border-radius: 6px;
    font-weight: 600;
    white-space: nowrap;
}
.player-link .avatar {
    vertical-align: middle;
    width:  calc(1.45em + 0.5em);    /* line-height(1.45em) + pad-y*2 */
    height: calc(1.45em + 0.5em);
}
.player-link:hover { text-decoration: none; }
.player-link:hover > span:not(.avatar) { background: #fff; }

/* Player edit form: avatar preview alongside the file input. Spans
   the full form width because the file input chrome doesn't fit
   neatly inside a half-width grid cell. */
.avatar-field {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    grid-column: 1 / -1;
    max-width: 100%;
    min-width: 0;
}
.avatar-field-input {
    display: flex;
    flex-direction: column;
    gap: 4px;
    flex: 1 1 0;
    min-width: 0;
}
.avatar-field input[type=file] {
    font-size: 13px;
    max-width: 100%;
}

.player-header { display: flex; align-items: center; gap: 18px; }
.player-header h2 { margin: 0 0 4px; font-size: 22px; color: var(--fg); text-transform: none; }
.player-header-meta { flex: 1; }

/* Inline avatar prompt shown right after a player RSVPs, when they
   haven't uploaded one yet. */
.rsvp-avatar-prompt {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 10px 12px;
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 8px;
    margin: 14px 0;
}
.rsvp-avatar-prompt-text { display: flex; flex-direction: column; min-width: 0; }
.rsvp-avatar-prompt input[type=file] { font-size: 13px; max-width: 100%; min-width: 0; flex: 1 1 0; }

/* Divider between the embedded game body and the season's own panels
   on the season page. Sits at the column max-width so it visually
   matches the rest of the page's content rhythm. */
.section-divider {
    height: 0;
    border: 0;
    border-top: 5px solid var(--muted);
    margin: 24px 0;
    width: 100%;
}

/* RSVP panel — four status buckets across the top, the player's own
   action row below, an admin manage section after that. */
.rsvp-buckets {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
}
.rsvp-bucket {
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 8px;
    padding: 10px 12px;
}
.rsvp-bucket h3 {
    margin: 0 0 6px;
    font-size: 12px;
    text-transform: uppercase;
    color: var(--muted);
    font-weight: 600;
    display: flex;
    align-items: baseline;
    gap: 8px;
}
.rsvp-bucket h3 .count {
    background: var(--panel);
    color: var(--fg);
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 11px;
}
.rsvp-bucket ul { list-style: none; padding: 0; margin: 0; }
.rsvp-bucket li { padding: 2px 0; }
.rsvp-bucket .empty { color: var(--muted); margin: 0; font-size: 13px; }
.rsvp-bucket.rsvp-in    h3 { color: var(--green); }
/* "Late" sits visually between in (green) and out (red), so it gets
   the muted cream — distinct from rsvp-out and from rsvp-icon-late. */
.rsvp-bucket.rsvp-late  h3 { color: var(--muted); }
.rsvp-bucket.rsvp-out   h3 { color: var(--accent-dark); }

.rsvp-mine {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 10px 12px;
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 8px;
    /* Single source of truth for the vertical gap between buckets,
       the player's own action row, and the admin manage panel. */
    margin: 14px 0;
}

/* Attention-grabbing pulse when the logged-in user hasn't RSVPed yet
   for the game they're looking at. The keyframe spends most of its
   cycle dim, then briefly flares a soft accent ring so it nudges the
   eye without strobing. */
@keyframes rsvp-attention {
    0%, 70%, 100% {
        box-shadow: 0 0 0 0 rgba(229, 133, 114, 0);
        border-color: var(--muted);
    }
    35% {
        box-shadow: 0 0 0 5px rgba(229, 133, 114, 0.35);
        border-color: var(--accent-dark);
    }
}
.rsvp-mine--needs-rsvp {
    animation: rsvp-attention 3.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
    .rsvp-mine--needs-rsvp { animation: none; border-color: var(--accent-dark); }
}
.rsvp-btn {
    background: var(--panel);
    color: var(--fg);
    border: 1px solid var(--muted);
    border-radius: 6px;
    padding: 6px 12px;
    font: inherit;
    cursor: pointer;
}
.rsvp-btn:hover { border-color: var(--accent-dark); }
.rsvp-btn.current {
    background: var(--accent-dark);
    border-color: var(--accent-dark);
    color: white;
}
.rsvp-btn.rm:hover { border-color: var(--accent-dark); color: var(--accent-dark); }

.rsvp-list td { padding: 4px 10px 4px 0; }
.rsvp-list .rm-cell { width: 1%; }
.rsvp-add { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.invite-toggle-all-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    user-select: none;
}

@media (max-width: 600px) {
    .rsvp-buckets { grid-template-columns: 1fr; }
}

/* Location block in the game edit form. The "+ Add a new location"
   inputs stay hidden until the dropdown switches to "new". */
.location-block #new-location-fields { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 8px; }
.location-block #new-location-fields.hidden { display: none; }
.meta .addr { color: var(--muted); }

/* Outbound messaging — flash banner after a redirect, plus the inline
   send-invite form on the player edit panel. */
.flash {
    background: var(--panel);
    border: 1px solid var(--accent-dark);
    color: var(--fg);
    padding: 8px 14px;
    border-radius: 8px;
    margin-bottom: 14px;
}
.send-invite,
.link-telegram { display: flex; gap: 8px; align-items: center; margin-top: 10px; flex-wrap: wrap; }
.send-invite input[type=text] { flex: 1 1 240px; }

/* Game-page mass-invite button: lives between the player's RSVP row and
   the admin manage panel. */
.mass-invite {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    /* Same vertical rhythm as .rsvp-mine — 14px above and below. */
    margin: 14px 0;
}
.mass-invite button {
    background: var(--accent-dark);
    color: white;
    border: 0;
    border-radius: 6px;
    padding: 8px 14px;
    font: inherit;
    cursor: pointer;
}
.mass-invite button:hover { filter: brightness(1.1); }

/* Login page — SMS form + Telegram bot link. Phone input mirrors the
   header search input so the page reads consistently. */
.login-sms { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.login-sms input[type=tel] {
    padding: 8px 12px;
    background: var(--panel-2);
    color: var(--fg);
    border: 1px solid var(--muted);
    border-radius: 8px;
    font: inherit;
    outline: none;
}
.login-sms input[type=tel]:focus { border-color: var(--accent-dark); }
.btn-link {
    display: inline-block;
    background: var(--accent-dark);
    color: white;
    padding: 8px 14px;
    border: 0;
    border-radius: 6px;
    font: inherit;
    font-weight: 600;
    text-transform: uppercase;
    cursor: pointer;
}
.btn-link:hover { filter: brightness(1.1); text-decoration: none; }

/* Tables — horizontal scroll on narrow screens */
.panel:has(table) { overflow-x: auto; }

/* Matrix leaderboard: wide tables with a narrow per-game column.
   The matrix panel respects the same column max-width as every other
   panel; the .scroller inside provides horizontal scrolling when the
   table is wider than the column. */
.panel:has(.matrix) > .scroller { overflow-x: auto; }

.matrix th, .matrix td { padding: 3px 5px; vertical-align: middle; font-size: 13px; }
/* Calling cards in the season leaderboard get a slight font-size bump
   so they (and their avatars, which scale off the same em) read taller
   than the surrounding 13px cells. */
.matrix .col-player .player-link { font-size: 15px; }
.matrix thead tr:first-child th { font-size: 11px; }
.matrix th a { color: var(--fg); }
.matrix th a:hover { color: var(--accent-dark); }
/* Dropped-score cells: grayed out so the "best N of M" rule is visible. */
.matrix td.dropped { color: var(--muted); }
/* Zebra shading on every other game column to aid scanning up/down. */
.matrix th.alt, .matrix td.alt { background: rgba(255,255,255,0.05); }

/* Admin-only champ-player override column. Tri-state segmented control. */
.matrix .col-override { text-align: center; min-width: 96px; }
.tri {
    display: inline-flex;
    border: 1px solid var(--muted);
    border-radius: 999px;
    overflow: hidden;
    background: var(--panel-2);
}
.tri input { position: absolute; opacity: 0; pointer-events: none; }
.tri label {
    cursor: pointer;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
    user-select: none;
}
.tri label:has(input:checked) { color: white; }
.tri label:nth-child(1):has(input:checked) { background: var(--accent-dark); }
.tri label:nth-child(2):has(input:checked) { background: var(--muted); }
.tri label:nth-child(3):has(input:checked) { background: var(--green-dark); }
.override-actions {
    padding: 10px 12px;
    display: flex;
    justify-content: flex-end;
}
.override-actions button {
    font: inherit;
    padding: 7px 16px;
    background: var(--accent-dark);
    color: white;
    border: 0;
    border-radius: 5px;
    cursor: pointer;
}

/* Edge columns are sticky-pinned on each side; the middle game-score
   columns scroll horizontally underneath them. None of these have
   min-widths — the JS at the bottom of the season view measures their
   actual content widths on load and writes them into the CSS vars
   below, so the sticky offsets always track the real layout. */
.matrix .col-rank,
.matrix .col-player,
.matrix .col-points,
.matrix .col-stack,
.matrix .col-override {
    position: sticky;
    z-index: 2;
    background: var(--panel-2);
}
.matrix .col-rank   { left: 0; }
.matrix .col-player { left: var(--col-rank-w, 0); box-shadow: var(--col-shadow); }
.matrix .col-points { right: 0; box-shadow: var(--col-shadow); }
.matrix .col-stack    { right: 0; }
.matrix .col-override { right: 0; box-shadow: var(--col-shadow); }
/* When optional right-edge columns are present, scoot the inner ones
   leftward by the dynamic width of the columns to their right. The
   rightmost keeps the box-shadow; inner ones drop it. */
.matrix.has-stack    .col-points    { right: var(--col-stack-w, 0); box-shadow: none; }
.matrix.has-stack    .col-stack     { box-shadow: var(--col-shadow); }
.matrix.has-override .col-points    { right: var(--col-stack-plus-override-w, 0); box-shadow: none; }
.matrix.has-override .col-stack     { right: var(--col-override-w, 0); box-shadow: none; }
/* Hover background carries to sticky cells too. */
.matrix tbody tr:hover .col-rank,
.matrix tbody tr:hover .col-player,
.matrix tbody tr:hover .col-points,
.matrix tbody tr:hover .col-stack,
.matrix tbody tr:hover .col-override {
    background: var(--panel-2);
}
/* Prize stacks under the rank number so the column can stay narrow. */
.col-rank .rank-prize {
    display: block;
    font-size: 11px;
    font-weight: 600;
    color: var(--fg);
    line-height: 1.1;
}

/* Live-game elimination controls in the rank cell. <details>/<summary>
   collapses the form behind a single × button; clicking expands the
   "knocked out by" + bust-kind picker right inside the cell. */
.col-rank .elim > summary {
    cursor: pointer;
    list-style: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px; height: 22px;
    border: 1px solid var(--muted);
    border-radius: 4px;
    color: var(--muted);
    font-size: 14px;
    line-height: 1;
}
.col-rank .elim > summary::-webkit-details-marker { display: none; }
.col-rank .elim > summary:hover { color: var(--fg); border-color: var(--accent-dark); }
.col-rank .elim[open] > summary { background: var(--accent-dark); color: var(--fg); border-color: var(--accent-dark); }
.col-rank .elim-form {
    display: flex;
    flex-direction: column;
    gap: 6px;
    align-items: stretch;
    padding: 8px 10px;
    margin-top: 6px;
    background: var(--panel-2);
    border: 1px solid var(--muted);
    border-radius: 6px;
    text-align: left;
    min-width: 180px;
    font-size: 13px;
    text-transform: none;
}
.col-rank .elim-form label { display: flex; flex-direction: column; gap: 2px; color: var(--muted); font-size: 11px; text-transform: uppercase; }
.col-rank .elim-form select { font: inherit; padding: 4px 6px; background: var(--panel-2); color: var(--fg); border: 1px solid var(--muted); border-radius: 4px; }
.col-rank .elim-form button { align-self: stretch; padding: 6px 10px; font-size: 12px; }

.col-rank .uneliminate { display: inline; }
.col-rank .uneliminate button {
    background: transparent;
    border: 0;
    color: var(--muted);
    cursor: pointer;
    padding: 0 0 0 4px;
    font-size: 14px;
    line-height: 1;
}
.col-rank .uneliminate button:hover { color: var(--fg); }
/* Sub-header row: month labels beneath each game column. */
.matrix thead tr.sub th {
    font-size: 11px;
    font-weight: 400;
    padding: 4px 8px;
    border-bottom: 1px solid var(--muted);
    color: var(--muted);
    text-transform: none;
}

/* Tournament clock — big-and-loud panel above the game body. State line
   ("CARDS UP IN" / "LEVEL N" / "PAUSED") on top, fat time digits in the
   middle, blinds + level notes underneath, and a thin progress bar
   showing how far into the current level we are. */
/* Top row of the game body — clock on the left fills remaining space,
   leaderboard panel on the right takes its natural content width.
   Flex's default align-items: stretch makes both boxes share the
   row's height (= max of the two), so they always match. Wraps to
   a single column when the clock would have to drop below ~320px. */
.game-top {
    display: flex;
    flex-wrap: nowrap;
    /* No cross-axis stretch. layout() pins clock and panel to the same
       targetH inline so they render the same height — and crucially,
       offsetHeight on each item reports the natural content height
       (not a stretched value) during the measurement phase, which is
       what tier F's "panelH > cap" comparison needs. */
    align-items: flex-start;
    gap: 16px;
    margin-bottom: 18px;
    container-type: inline-size;
}
/* When the clock is rendered, force a definite row height of one
   viewport minus the sticky header. Gives the clock a real height
   to size its digits against (cqh / clientHeight only behave when
   the container has a definite height, not a content-driven one).
   :has() narrows this to live games only — complete-game pages
   without a clock keep their natural row height. */
.game-top:has(.game-clock) { max-height: calc(100vh - var(--header-h, 64px)); }

.game-top > * { min-width: 0; min-height: 0; }
/* Sizes here are baseline only — clock.js's layout() owns the final
   heights. It picks one of four modes per layout pass:
     1. wrapped + content > cap  → cap .results-scroll, clock stays small
     2. wrapped + content ≤ cap  → grow clock to fill remaining space
     3. side-by-side, panel ≤ cap → clock height = panel height
     4. side-by-side, panel > cap → cap .results-scroll, clock = full cap
   Everything plumbs through inline styles set on .game-clock,
   .game-top > .panel, and .results-scroll. */
.game-top > .game-clock {
    /* Clock fills whatever's left after the panel takes its content
       width. Basis 0 + grow 1 means it doesn't claim any preferred
       width of its own; squeeze in row mode shrinks the clock first
       (down to the JS-detected stacking threshold) before the panel
       has to give up any ground. */
    flex: 1 1 0;
    margin: 0;
    overflow: hidden;
}
.game-top > .panel {
    /* Panel never shrinks — it sits at its content width so the
       leaderboard table never gets a horizontal scrollbar in
       side-by-side mode. layout() flips .game-top to column when the
       clock would be too thin, which moves the panel onto its own line
       at full width via the column-axis cross-stretch. */
    flex: 0 0 auto;
    margin: 0;
    padding: 0;
    background: transparent;
    border-radius: 0;
    /* Override .panel:has(table) { overflow-x: auto } — that rule
       implicitly promotes overflow-y to auto (per CSS spec, a non-
       visible value on one axis forces the other from visible to
       auto), which would scroll the WHOLE panel when the panel is
       cap-pinned and content exceeds it. We want the inner
       .results-scroll to be the only vertical scroll surface. */
    overflow: visible;
}
/* Wrapper around the leaderboard table inside .game-top > .panel.
   layout() sets max-height + overflow-y when the leaderboard would
   otherwise blow past the row cap; sticky thead keeps column headers
   visible while the body scrolls. Inert outside .game-top — the same
   wrapper is rendered on the season page where no scroll is wanted. */
.game-top > .panel .results-scroll {
    min-height: 0;
    /* Block the implicit overflow-x: auto promotion that would
       otherwise fire when JS sets overflow-y: auto. The vertical
       scrollbar's gutter steals ~12px of inner width — without this
       hidden, the table overflows by exactly that much and a
       greyed-out horizontal scrollbar appears with no useful range. */
    overflow-x: hidden;
}
.game-top > .panel .results-scroll table.results thead th {
    position: sticky;
    top: 0;
    background: var(--panel-2);
    z-index: 1;
}
/* Stacking is owned by clock.js's layout() — no media/container query
   here, so there's no sudden flip at a fixed width. layout() measures
   row-mode panel overflow + clock width and writes
   flex-direction: column inline when stacking is needed. */

.game-clock {
    background: var(--accent-dark);
    border: 1px solid var(--muted);
    border-radius: 12px;
    margin: 0 0 18px;
    text-align: center;
    color: var(--fg);
    position: relative;
    overflow: hidden;
    /* Stack the clock contents (state / time / blinds / notes /
       progress / pause) and center them vertically. When the clock
       sits next to a taller leaderboard panel and stretches to match,
       the contents float in the middle of the panel rather than
       hugging the top. */
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.game-clock-state {
    font-size: clamp(14px, 2.5vw, 24px);
    text-transform: uppercase;
    color: var(--panel-2);
    background: var(--fg);
    font-weight: 700;
    line-height: 1.1;
}
.game-clock-max-text {
    flex-grow: 1;
    /* Allow flex-shrink past content size on very short viewports —
       without this, the wrapper holds its natural ~90px floor and
       the clock overflows its capped row instead of compressing. */
    min-height: 0;
    /* Center time + blinds within whatever vertical space the wrapper
       has. When fonts are smaller than the available height, the lines
       sit in the middle of the wrapper rather than hugging the top. */
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.game-clock-time {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    /* JS scales font-size up on each tick to fill the panel width. The
       initial value is intentionally conservative — small enough that
       the natural clock height doesn't push .game-top into wrapping at
       viewport widths where side-by-side would still fit, and small
       enough that the brief pre-fit moment never overflows. */
    font-size: 24px;
    line-height: 1;
    font-weight: 700;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
}
.game-clock-blinds {
    margin-top: 6px;
    /* Conservative initial size; JS scales up to fill on first tick.
       Same reasoning as .game-clock-time — start small, grow up. */
    font-size: 14px;
    line-height: 1;
    font-weight: 700;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-align: center;
}
.game-clock-blinds .sb,
.game-clock-blinds .bb { font-weight: 700; }
.game-clock-blinds .sep {
    color: var(--muted);
    margin: 0 0.15em;
    font-weight: 400;
}
/* Level/break notes — a small subordinate line under the headline. */
.game-clock-notes {
    margin-top: 6px;
    font-size: 12px;
    color: var(--muted);
    text-transform: uppercase;
    min-height: 1em;
}
.game-clock-notes:empty { display: none; }
.game-clock-progress {
    margin: 18px auto 6px;
    height: 8px;
    width: 100%;
    max-width: 480px;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 4px;
    position: relative;
    /* No overflow:hidden — the playhead marker on the bar deliberately
       extends taller than the track. */
}
.game-clock-progress-bar {
    display: block;
    height: 100%;
    width: 0;
    background: var(--fg);
    border-radius: 4px 0 0 4px;
    position: relative;
    transition: width 0.5s linear;
}
/* Vertical playhead at the boundary between completed and remaining
   time. Sticks above and below the track for emphasis. */
.game-clock-progress-bar::after {
    content: '';
    position: absolute;
    right: -2px;
    top: -7px;
    bottom: -7px;
    width: 4px;
    background: var(--fg);
    border-radius: 2px;
}
/* Small "Next: X / Y" or "Next: 20m Break" line under the progress bar. */
.game-clock-next {
    font-size: clamp(16px, 2.4vw, 24px);
    color: var(--muted);
}
.game-clock-next:empty { display: none; }
/* Pre-game state: nothing to track yet, so suppress the progress bar
   and the "Next:" tease until cards are up. */
.game-clock--pre-game .game-clock-progress,
.game-clock--pre-game .game-clock-next { display: none; }
.game-clock-pause {
    margin-top: 14px;
}
.game-clock-pause button {
    font-size: 12px;
    padding: 6px 16px;
}
.game-clock--final .game-clock-time { color: var(--muted); }

/* Page-wide mute toggle pinned to bottom-left. Starts in the muted state
   (X icon, dimmed). One click anywhere on the site swaps to the speaker
   icon and unmutes; thereafter clicking the button toggles. */
.bcc-mute {
    position: fixed;
    left: 12px;
    bottom: 12px;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    border: 1px solid var(--muted);
    background: rgba(20, 24, 30, 0.85);
    color: var(--fg);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 50;
    padding: 0;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.bcc-mute:hover { color: var(--accent-dark); }
.bcc-mute .bcc-mute-on,
.bcc-mute .bcc-mute-off { display: none; }
.bcc-mute[data-muted="0"] .bcc-mute-on  { display: block; }
.bcc-mute[data-muted="1"] .bcc-mute-off { display: block; color: var(--muted); }

/* Blind structure editor inside Edit Game. Nested <details> so it
   stays collapsed by default; the preset bar / grid / save-as-preset
   block only render when the admin opens it. */
details.blind-editor { margin-top: 4px; }
details.blind-editor > summary {
    cursor: pointer;
    font-weight: 600;
    font-size: 12px;
    text-transform: uppercase;
    color: var(--muted);
    padding: 6px 0;
    user-select: none;
}
details.blind-editor[open] > summary { color: var(--fg); margin-bottom: 8px; }
.blind-preset-bar {
    display: flex;
    gap: 10px;
    align-items: end;
    flex-wrap: wrap;
    margin-bottom: 10px;
}
.blind-preset-bar label { display: flex; flex-direction: column; gap: 4px; }
.blind-preset-bar select { min-width: 180px; }
.blind-preset-delete:disabled { opacity: 0.4; cursor: not-allowed; }
.blind-grid { width: 100%; }
.blind-grid th { text-align: left; font-size: 11px; color: var(--muted); }
.blind-grid td.lv { width: 32px; color: var(--muted); font-variant-numeric: tabular-nums; }
.blind-grid td input[type="number"][name$="[mins]"] { min-width: 5ch;  padding-left: 4px; padding-right: 2px; }
.blind-grid td input[type="number"][name$="[big]"]  { min-width: 10ch; padding-left: 4px; padding-right: 2px; }
.blind-grid tr.is-break td input[type="number"][name$="[big]"] { display: none; }
.blind-grid td input[type="text"]   { width: 100%; min-width: 140px; }
.blind-grid td.rm-cell { width: 24px; }
/* Beats the generic last-child width on admin-edit mini-tables. The
   column shrinks to fit the buttons; the inner flex div holds spacing
   constant regardless of whitespace between the button tags. */
.admin-edit table.mini-table td.row-controls {
    width: 1%;
    white-space: nowrap;
    padding-left: 6px;
}
.blind-grid .row-ctrl {
    display: flex;
    justify-content: flex-end;
    gap: 2px;
}
.blind-grid .row-ctrl .row-mv,
.blind-grid .row-ctrl .rm {
    flex: 0 0 auto;
    background: transparent;
    border: 1px solid var(--muted);
    color: var(--muted);
    width: 24px;
    height: 24px;
    padding: 0;
    border-radius: 4px;
    cursor: pointer;
    line-height: 1;
    font-size: 14px;
}
.blind-grid .row-ctrl .row-mv:hover,
.blind-grid .row-ctrl .rm:hover { color: var(--fg); border-color: var(--accent-dark); }
.blind-grid-actions { margin: 8px 0 14px; }
.blind-preset-save {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    border-top: 1px dashed var(--muted);
    padding-top: 10px;
}
.blind-preset-save .blind-preset-name { flex: 0 1 220px; }
.blind-preset-save .hint { flex-basis: 100%; }

