/* 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 */
    --blue: #2d4f7d;       /* poker-chip blue — table 1 seat badge */
    --col-shadow: 0 0 6px rgba(0, 0, 0, 0.4);  /* drop shadow for matrix sticky cols */
}

* { box-sizing: border-box; }

/* Themed scrollbars. Firefox + modern Chrome read the standard
   scrollbar-* shorthands; older WebKit picks up the pseudo-element
   block below. The thumb's transparent border + background-clip
   keeps a 2px visual gap between thumb and track edge so the bar
   reads as a tidy pill instead of filling the full gutter. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--muted) transparent;
}
::-webkit-scrollbar         { width: 10px; height: 10px; }
::-webkit-scrollbar-track   { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--muted);
    border: 2px solid transparent;
    border-radius: 5px;
    background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--fg); }
::-webkit-scrollbar-corner  { background: transparent; }

html, body {
    margin: 0;
    padding: 0;
    background: var(--panel-2);
    color: var(--fg);
    font: 16px/1.45 'Roboto Condensed', 'Arial Narrow', 'Helvetica Neue', sans-serif;
    /* Disable browser scroll anchoring — mobile Chrome's anchor
       hunting reacts to sub-pixel reflows from the per-tick font
       refit and yanks the scroll position back, even when the page
       height hasn't actually changed. */
    overflow-anchor: none;
    /* 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; }

/* Group of right-side header actions (search + profile/sign-in). Wrapped
   together so they always wrap as a unit when the header runs out of
   horizontal room — the search button never ends up stranded on its
   own line away from the profile icon. margin-left: auto pushes the
   whole group to the right end of the header row. */
.header-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 18px;
}

/* When search expands, take over the whole header row and hide the
   other items — both the siblings outside the group (brand, crumbs)
   and the profile icon inside the group. */
.header-actions:has(.search.open) {
    flex: 1 1 100%;
    margin-left: 0;
}
.header-actions:has(.search.open) > :not(.search) { display: none; }

/* Header search — sits inside .header-actions; expands to fill the row
   when opened. Always shows as a magnifying-glass icon by default;
   tapping expands the input (which takes over the header row by
   hiding brand/crumbs/profile). */
.search {
    position: relative;
    display: flex;
    align-items: center;
}
.search input[type=search] {
    display: none;        /* shown only when .search.open */
    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: inline-flex;
    align-items: center;
    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.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(.header-actions) { display: none; }
.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); }

/* 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-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;
    }
    .header-actions { gap: 10px; }
    .brand { padding: 4px 10px; }
    .brand img { height: 28px; }

    .crumbs { font-size: 16px; }
    .crumbs .sep { margin: 0 5px; }
}

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); }

/* Seat badge — small colored disk after the player's calling card
   showing which table/seat they're at. One color per table: blue
   (t1), charcoal (t2), red (t3). Sized in em so it scales with the
   surrounding row's font-size. */
.seat-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6em;
    height: 1.6em;
    border-radius: 50%;
    font-size: 0.85em;
    font-weight: 700;
    color: var(--fg);
    margin-left: 8px;
    vertical-align: middle;
}
.seat-badge.t1 { background: var(--blue); }
.seat-badge.t2 { background: var(--panel); }
.seat-badge.t3 { background: var(--accent-dark); }
/* Empty/unseated state — dashed muted ring with no fill, distinct
   from any of the table colors so unset entries read as "no seat
   assigned yet" at a glance. */
.seat-badge.empty {
    background: transparent;
    border: 1px dashed var(--muted);
    color: var(--muted);
}

/* Admin-only: the seat badge is also a button that opens the seat
   modal. Inherit the badge styling above and just layer the button
   reset + cursor. */
button.seat-trigger {
    padding: 0;
    font: inherit;
    cursor: pointer;
    text-transform: none;
}
button.seat-trigger:hover { filter: brightness(1.15); }
button.seat-trigger.empty:hover {
    border-color: var(--fg);
    color: var(--fg);
}
.free-footnote { color: var(--muted); margin: 6px 0 0; }
/* "* free entry" + "Out: …" sit on one line when both are present;
   wrap to two when the viewport is too narrow. Margins zeroed so the
   parent's gap is the only spacing between them. Single font-size on
   the container so neither footnote reads as smaller than the other. */
.leaderboard-footnotes {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 16px;
    align-items: baseline;
    font-size: 13px;
}
.leaderboard-footnotes > p { margin: 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 {
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}
.leaderboard { width: 100%; }
/* .results sizes to its content. The previous 100%-fill stretched
   sparse leaderboards (few entries / no Cash column) into a sea of
   empty padding; letting it pick its natural width keeps the columns
   tight and lets the .results-scroll wrapper handle overflow when
   there's actually too much. */
.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; }
/* Player column stays on a single line — calling card, late mark,
   seat badge, improvement badges all hug the player's name on the
   same row. The .results-scroll wrapper has overflow-x: auto, so
   if a too-long line ever materializes it scrolls horizontally
   instead of breaking onto two rows. */
.results td.col-player { white-space: nowrap; }
/* Shrink .results-scroll to fit its table in both modes — column
   sees it centered in a wide panel, row sees the panel column itself
   collapse to the table's width so the clock takes the rest. max-width:
   100% caps the wrapper at the panel's available width; once the
   table outgrows that, overflow-x: auto inside takes over and scrolls
   horizontally. */
.game-top .results-scroll {
    width: fit-content;
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
}
/* 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 .head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 6px;
}
/* Breathing room between two payouts strips when they stack
   (championship + leaderboard split, season page only). Single-block
   uses (game page) aren't affected — the adjacent-sibling selector
   only fires when a second .payouts-preview follows. */
.payouts-preview + .payouts-preview { margin-top: 18px; }
.payouts-preview .head h2 { margin: 0; }
.payouts-preview .label {
    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: 128px; height: 128px; }
.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;
    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%;
}
.avatar-field .avatar-preview {
    position: relative;
    display: inline-flex;
    line-height: 0;
}
.avatar-delete-btn {
    position: absolute;
    top: -6px;
    right: -6px;
    box-sizing: border-box;
    width: 24px;
    height: 24px;
    padding: 0;
    margin: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 2px solid var(--panel);
    background: #b32424;
    color: #fff;
    font: 16px/1 sans-serif;
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.avatar-delete-btn:hover { background: #d23030; }

/* ELO leaderboard on the league page: a small horizontal bar packed
   into the rating cell so the rank order is visually obvious. The bar
   pegs to the player's percentile within the displayed range
   (--elo-pct, set inline) so the worst still has a sliver and the best
   is the full width. */
.elo-board td.elo-cell {
    position: relative;
    min-width: 80px;
    padding-right: 18px;
}
.elo-board .elo-bar {
    position: absolute;
    left: 0;
    bottom: 2px;
    height: 4px;
    width: var(--elo-pct, 0%);
    background: var(--accent-dark);
    border-radius: 2px;
    opacity: 0.55;
}
.elo-board .elo-num {
    position: relative;     /* stack above the bar */
}

/* Player-page ELO line chart. SVG fills the panel; preserveAspectRatio
   on the element handles letterboxing on tiny screens. */
.elo-chart-panel { padding-bottom: 16px; }
.elo-chart {
    display: block;
    width: 100%;
    aspect-ratio: 800 / 220;
    color: var(--fg);
}
.elo-grid { stroke: var(--muted); stroke-opacity: 0.25; }
.elo-tick { fill: var(--muted); font-size: 11px; }
.elo-line { stroke: var(--accent-dark); stroke-width: 4; stroke-linejoin: round; stroke-linecap: round; }
.elo-node { fill: var(--accent-dark); stroke: var(--fg); stroke-width: 3; cursor: pointer; }
.elo-node-link { cursor: pointer; }
.elo-chart-wrap { position: relative; }
/* Floating tooltip placed by JS — itself a link to the same game so a
   touchscreen user can tap the dot (which just pins the tooltip) and
   then tap the tooltip to follow through. translate(-50%, -100%)
   anchors it above the dot; the upward margin keeps it from kissing
   the node. pointer-events: auto so the link inside is clickable. */
.elo-tooltip {
    position: absolute;
    transform: translate(-50%, -100%);
    margin-top: -8px;
    background: var(--fg);
    color: var(--panel-2);
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
    z-index: 5;
    cursor: pointer;
}
.elo-tooltip[hidden] { display: none; }
/* Underline just the game-name portion so the link target reads
   clearly; the date/rating tail stays plain. */
.elo-tooltip-name { text-decoration: underline; }

/* Trophy row in the player header. Each tile is icon-on-top, with a
   thin tier-progress strip across the bottom: current tier on the
   left, next tier (greyed) on the right, progress bar between them
   pegged to count / next-tier (so 38 games reads as "38% toward 100x").
   Podium trophies skip the strip entirely — the medal emoji conveys
   the single tier. */
.trophies {
    list-style: none;
    margin: 16px 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}
.trophy {
    width: 256px;
    height: 256px;
    background: var(--panel);
    border: 1px solid var(--muted);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    cursor: default;
    overflow: hidden;
}
/* Always-visible label across the top — same dark strip background as
   the progress strip below so the icon is sandwiched between two
   matching bands. */
.trophy-label {
    width: 100%;
    box-sizing: border-box;
    padding: 8px 10px;
    background: rgba(0, 0, 0, 0.22);
    color: var(--fg);
    font-size: 17px;
    font-weight: 700;
    line-height: 1;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 0 0 auto;
}
.trophy-icon {
    font-size: 70px;
    line-height: 1;
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    padding-top: 4px;
}
.trophy.has-strip .trophy-icon { padding-top: 18px; padding-bottom: 18px; }

/* Locked trophies sit at the end of the list. Only the emoji is
   dimmed (grayscale + lower opacity) — the progress strip stays at
   full color so the goal post reads cleanly. */
.trophy.is-locked .trophy-icon {
    filter: grayscale(1);
    opacity: 0.45;
}
.trophy.is-locked .trophy-tier.next { color: var(--muted); opacity: 1; }

.trophy-strip {
    width: 100%;
    height: 64px;
    flex: 0 0 64px;
    box-sizing: border-box;
    padding: 8px 8px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    background: rgba(0, 0, 0, 0.22);
}
/* Achievement ladder — three medals always rendered, achieved = full
   color, unachieved = greyed + dimmed. Sits right below the label and
   shares its dark band background, so the upper portion of the trophy
   reads as one combined header. */
.trophy-medals {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    justify-items: center;
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px 12px;
    flex: 0 0 auto;
    background: rgba(0, 0, 0, 0.22);
}
.trophy-medals .t-medal {
    font-size: 24px;
    line-height: 1;
    filter: grayscale(1);
    opacity: 0.35;
}
.trophy-medals .t-medal.is-achieved {
    filter: none;
    opacity: 1;
}
/* Bottom row: medal+count on the left, progress bar in the middle,
   medal+count on the right. Each end cell stacks the medal above its
   count vertically. */
.trophy-bar-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    column-gap: 6px;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.2;
}
.trophy-tier {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    white-space: nowrap;
    min-width: 0;
}
.trophy-tier.curr { color: var(--green); }
.trophy-tier.next { color: var(--muted); opacity: 0.85; }
.trophy-tier .t-medal {
    display: inline-flex;
    line-height: 1;
    font-size: 18px;
}
/* Double-gold: stack the second 🥇 with a heavy overlap and apply a
   warm glow so it pops away from the muted next-tier color. */
.trophy-tier .t-medal.is-double .t-medal-stacked {
    margin-left: -0.75em;
    filter: drop-shadow(0 0 3px rgba(212, 175, 55, 0.85));
}
.trophy-progress {
    height: 7px;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 3px;
    overflow: hidden;
}
.trophy-progress-fill {
    height: 100%;
    background: var(--green);
    border-radius: 3px;
}

/* Two-step magic-link sign-in: GET renders a confirmation page (so
   link-preview scrapers don't burn the token), POST consumes it. The
   panel itself spans full width like every other .panel; the inner
   bits cap at 360px and center themselves so the form doesn't stretch
   on a wide viewport. (Putting max-width on the panel would collide
   with the column-centering padding inherited from main > * and
   collapse the content area.) Min-height + flex-center pulls the
   form into the vertical middle of the viewport on desktop instead
   of leaving the bottom half empty. */
.login-confirm {
    min-height: calc(100vh - var(--header-h, 64px) - 60px);
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.login-confirm > * {
    width: 100%;
    max-width: 360px;
    margin-left: auto;
    margin-right: auto;
}
.login-confirm h1   { text-align: center; }
.login-confirm .hint { text-align: center; }
.login-confirm-who {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    margin: 16px auto;
}
.login-confirm-name {
    font-size: 22px;
    font-weight: 700;
    color: var(--fg);
}
.login-confirm-form {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 20px;
}
.login-confirm-go,
.login-confirm-cancel {
    width: 100%;
    padding: 16px 20px;
    font-size: 16px;
}
.login-confirm-cancel {
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--muted);
}
.login-confirm-cancel:hover { color: var(--fg); border-color: var(--fg); }

/* Stacked panel: avatar+meta row up top, trophies row below. The
   inner .player-header-top owns the row layout so the trophies aren't
   competing with flex-wrap heuristics. */
.player-header { display: flex; flex-direction: column; }
.player-header-top { 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 — the player's own action row + an admin manage section.
   The in/late/out lists are folded into the leaderboard itself: late
   RSVPers get a clock icon next to their name; out RSVPers are listed
   below the leaderboard as a comma-separated line. */
.rsvp-late-mark {
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    color: var(--muted);
    vertical-align: middle;
}
.rsvp-out-list {
    margin: 8px 12px 0;
    color: var(--muted);
}
.rsvp-out-list strong { color: var(--fg); }
.rsvp-out-list a { color: inherit; }

.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;
}


/* 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; }
/* Pill-shaped little card for inline factoids (date, location, buy-in
   etc. on the game meta panel). Mirrors the calling-card chip used on
   .player-link so meta items read as taps/badges instead of labelled
   prose — labels are dropped because the value alone is unambiguous
   in context (a date looks like a date, etc.). */
.info-card {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--fg);
    color: var(--accent-dark);
    padding: 0.25em 0.75em;
    line-height: 1.45;
    border-radius: 6px;
    font-weight: 600;
    white-space: nowrap;
}
/* Address tucked inside the location card after the name. Dimmed via
   opacity (so it stays in the burnt-red palette of the pill) and
   un-bolded so the location name reads as the primary label. */
.info-card .addr {
    font-weight: 400;
    opacity: 0.7;
}

/* 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 control in the rank cell — a small × button
   that opens the shared .elim-modal <dialog>. The form fields used to
   live in this cell, but it's far too narrow to host a select and a
   bust-kind picker without crowding; the modal gives them a real
   surface to render on. */
.col-rank .elim-trigger {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px; height: 22px;
    padding: 0;
    background: transparent;
    border: 1px solid var(--muted);
    border-radius: 4px;
    color: var(--muted);
    font-size: 14px;
    line-height: 1;
    text-transform: none;
}
.col-rank .elim-trigger:hover {
    color: var(--fg);
    border-color: var(--accent-dark);
}

/* Shared elimination dialog. The native <dialog> + ::backdrop handles
   modality and click-outside-to-dismiss when paired with the JS in
   admin.js. Two stacked forms inside: the main "Bust" form (KO-by +
   bust-kind selectors) and the secondary "Delete entry" form for
   when an entry was recorded against the wrong player and just needs
   to go. */
.elim-modal {
    background: var(--panel);
    color: var(--fg);
    border: 1px solid var(--muted);
    border-radius: 8px;
    padding: 0;
    width: min(420px, 92vw);
    max-height: 90vh;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
.elim-modal::backdrop {
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(2px);
}
.elim-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--muted);
}
.elim-modal-header h3 {
    margin: 0;
    font-size: 16px;
    text-transform: uppercase;
    color: var(--muted);
}
.elim-modal-header h3 .elim-modal-name {
    color: var(--fg);
    text-transform: none;
}
.elim-modal-close {
    background: transparent;
    border: 0;
    color: var(--muted);
    cursor: pointer;
    font-size: 20px;
    line-height: 1;
    padding: 4px 8px;
}
.elim-modal-close:hover { color: var(--fg); }

.elim-modal .elim-form {
    display: flex;
    flex-direction: column;
    gap: 12px;
    align-items: stretch;
    padding: 16px;
}
.elim-modal .elim-form label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    color: var(--muted);
    font-size: 11px;
    text-transform: uppercase;
}
.elim-modal .elim-form select {
    font: inherit;
    padding: 8px 10px;
    background: var(--panel-2);
    color: var(--fg);
    border: 1px solid var(--muted);
    border-radius: 4px;
}
.elim-modal .elim-form button {
    align-self: stretch;
    padding: 10px 16px;
}
/* One button per bust outcome — each submits the form directly with
   its own bust_kind value, so the admin doesn't pick from a select
   then click Bust. Stack vertically; align-self: stretch from the
   rule above carries through since they're flex children. */
.elim-modal .elim-form-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.elim-modal .elim-delete-form,
.elim-modal .set-paid-form {
    /* Tighter top so secondary forms (Paid select, Delete entry) hug
       the form above instead of doubling the gap with their own
       padding-top. */
    padding: 0 16px 16px;
}
.elim-modal .elim-delete-btn {
    width: 100%;
    padding: 8px 16px;
    font-size: 12px;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--muted);
}
.elim-modal .elim-delete-btn:hover {
    color: var(--fg);
    border-color: var(--accent-dark);
}

/* Rank-cell trigger: the place number itself is the button that opens
   the rank modal (un-eliminate + delete-entry actions). Same boxed
   shape as .elim-trigger so the two read as the same kind of admin
   affordance — only the glyph inside differs. min-width over fixed
   width so two-digit places ("12") still fit comfortably. */
.col-rank .rank-trigger {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 4px;
    background: transparent;
    border: 1px solid var(--muted);
    border-radius: 4px;
    color: var(--muted);
    font-size: 14px;
    line-height: 1;
    text-transform: none;
}
.col-rank .rank-trigger:hover {
    color: var(--fg);
    border-color: var(--accent-dark);
}
/* Red $ overlay on the top-right corner of an admin-actionable
   X / rank button when the entry hasn't been marked paid. Lets admins
   scan the leaderboard for who still owes without opening every
   modal. The trigger buttons set position: relative implicitly via
   inline-flex so absolute positioning anchors against them. */
.col-rank .elim-trigger,
.col-rank .rank-trigger {
    position: relative;
    overflow: visible;
}
.col-rank .unpaid-badge {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 14px;
    height: 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--accent-dark);
    color: #fff;
    border: 1px solid var(--panel);
    border-radius: 50%;
    font-size: 8px;
    line-height: 1;
    font-weight: 700;
    pointer-events: none;
}
/* 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. */
/* Layout direction is decided by clock.js's layout() — it adds .is-row
   when innerWidth >= innerHeight (landscape), .is-col otherwise. The
   class swap drives all the sizing here; JS only handles font fitting. */
.game-top {
    position: relative;  /* anchor for .game-top-fs */
    gap: 16px;
    /* Breathing room above the clock/leaderboard so they aren't flush
       against whatever panel sits directly above (was implicit when
       .game-top was main's first child; now the RSVP panel is). */
    padding-top: 14px;
    margin-bottom: 18px;
}

/* Fullscreen toggle, top-right of .game-top. JS unhides it when the
   browser supports the Fullscreen API; otherwise it stays [hidden]. */
.game-top-fs {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 5;
    width: 32px;
    height: 32px;
    padding: 0;
    margin: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.35);
    color: #fff;
    font: 18px/1 sans-serif;
    cursor: pointer;
}
.game-top-fs:hover { background: rgba(0, 0, 0, 0.6); }

/* When .game-top itself is the fullscreen element: paint the app bg
   (browsers default fullscreen bg to black, which clashes with our
   palette), drop the header gap from the height cap, and add a bit of
   inner padding so the clock/leaderboard don't kiss the screen edge. */
.game-top:fullscreen,
.game-top.is-row:fullscreen,
.game-top.is-col:fullscreen {
    background: var(--panel-2);
    padding: 16px;
    max-height: 100vh;
    width: 100vw;
}

/* Landscape: side-by-side. .game-top is content-sized but capped at
   one viewport minus the sticky header. align-items: stretch makes the
   clock match the panel height, so the red box only grows as tall as
   the leaderboard (or its own content, whichever is larger), capped at
   the viewport. */
.game-top.is-row {
    display: grid;
    grid-template-columns: auto auto;
    /* Three rows: title, leaderboard (1fr), and footnotes wrapper that
       holds "* free entry" + "Out: …" side by side. Clock owns col 1
       across all three rows. The footnotes row collapses to 0 when
       neither note is present (auto track with no content). */
    grid-template-rows: auto minmax(0, 1fr) auto;
    max-height: calc(100vh - var(--header-h, 64px));
    /* Cap to parent's available width — without this, a block-level
       grid expands to its min-content size when content (the clock's
       white-space: nowrap text) gets too big, so the container grows
       past <main> and scrollWidth tracks clientWidth, hiding the
       overflow from the JS fitter. With width: 100% the grid stays
       inside main and oversize columns leak past clientWidth, so the
       fitter's x-overflow check on .game-top fires correctly. */
    width: 100%;
    /* Allow the grid track to shrink below the panel's intrinsic
       max-content (default is min-content). Without this the panel's
       leaderboard table can never shrink below its widest row, which
       again lets the grid grow past <main>. */
    min-width: 0;
}
.game-top.is-row > * { min-width: 0; }

.game-top > .game-clock {
    /* Override the base .game-clock { margin: 0 0 18px } — inside
       .game-top, gap handles spacing, and the leftover 18px would
       leave the clock visibly taller than the panel in side-by-side. */
    margin: 0;
}

.game-top.is-row > .game-clock {
    grid-column: 1;
    grid-row: 1 / -1;
}

/* Shared panel setup — both modes treat .panel as a flex column with
   .results-scroll as the only shrinkable child. The list's
   min-height: 120px contributes to the panel's automatic content-min
   (min-height: auto on flex items), so flex-shrink in either mode
   stops at chrome + 120 — no JS budgeting needed. */
.game-top > .panel {
    margin: 0;
    padding: 0;
    background: transparent;
    border-radius: 0;
    /* Block the implicit overflow-y: auto promotion from
       .panel:has(table) { overflow-x: auto } — vertical scrolling
       belongs to .results-scroll, not the whole panel. */
    overflow: visible;
    display: flex;
    flex-direction: column;
}

/* When admin can edit Paid/AO, game_results.php wraps .results-scroll
   in a <form>. Without this, the form would be the panel's direct flex
   item — flex on .results-scroll wouldn't apply where it matters, and
   the panel's natural-height squeeze would shrink h2/payouts instead
   of the list. display: contents pulls .results-scroll up to be a
   direct flex item of the panel (the form's submit semantics stay
   intact). */
.game-top > .panel > form {
    display: contents;
}

/* Everything in the panel except .results-scroll holds its natural
   size — only the list gives way when there's not enough room. */
.game-top > .panel > *:not(.results-scroll):not(form) {
    flex-shrink: 0;
}

/* Scope to descendants of .game-top — that's the only place the
   leaderboard wrapper needs a height floor + scroll behavior. The
   previous :last-child variant broke as soon as a sibling element
   landed after it (the elim-modal <dialog>, etc.), and didn't
   meaningfully apply on the season page either, where .results-scroll
   sits inside its own .panel without a height-bound parent. */
.game-top .results-scroll {
    min-height: 50px;
    overflow-y: auto;
    overflow-x: auto;
    /* Opt out of browser scroll anchoring — the clock fitter mutates
       layout on every tick, which makes Chrome/Firefox nudge scrollTop
       a few px back to keep its chosen anchor visible. That fights our
       autoscroll and reads as a jitter / upward drift. body already
       opts out for the same reason; this scroll container needs its
       own opt-out. */
    overflow-anchor: none;
}

/* Side-by-side specifics. */
.game-top.is-row > .panel {
    flex: 0 0 auto;
    min-width: 0;
    /* Cap panel main-axis (height) at viewport. align-items: stretch
       on the row syncs both items' heights, but a tall leaderboard
       would still push the row past the viewport without this cap. */
    max-height: calc(100vh - var(--header-h, 64px));
}

/* Stacked: definite height (= viewport - header) so flex-shrink
   actually fires when content exceeds it. max-height alone leaves the
   container indefinite for flex purposes — items wouldn't shrink, and
   the panel + list would just overflow into the layout below. With
   height: cap, the panel can shrink toward its content min and
   .results-scroll inside it absorbs the squeeze and scrolls. */
.game-top.is-col {
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - var(--header-h, 64px));
}

/* Sticky thead — the .results-scroll wrapper is the scroll container;
   pinning thead to its top keeps column headers visible as the rows
   scroll underneath, in both row mode (results-scroll fills the 1fr
   grid cell) and col mode (it shrinks via max-height). Solid bg so
   the alternating row stripes don't bleed through. */
.game-top .results-scroll table.results thead th {
    position: sticky;
    top: 0;
    background: var(--panel-2);
    z-index: 1;
}

/* Register --progress as a typed custom property so it can be
   transitioned smoothly. Without @property the CSS engine treats it
   as an opaque string and snaps between values. Modern browsers
   honor this; old ones just snap, which is a fine fallback. */
@property --progress {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

.game-clock {
    /* The clock background IS the progress bar: accent-dark fills from
       the left as the current level burns down, with the rest of the
       box showing as a lighter coral until it's eaten. JS sets
       --progress on this element each tick. */
    background: linear-gradient(
        to right,
        var(--accent-dark) 0%,
        var(--accent-dark) var(--progress),
        #bc5910 var(--progress),
        #bc5910 100%
    );
    transition: --progress 0.5s linear;
    border-radius: 12px;
    margin: 0 0 18px;
    text-align: center;
    color: var(--fg);
    position: relative;
    /* Stack the clock contents (state / time / blinds / notes /
       next / 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;
}
/* Solid background on pre-game and complete states — no level burning
   down, so the gradient progress treatment doesn't apply. Override
   wins because of selector specificity (class on .game-clock). */
.game-clock--pre-game,
.game-clock--final {
    background: var(--accent-dark);
}
.game-clock-state {
    font-size: clamp(20px, 3.5vw, 36px);
    border-top-left-radius: 12px;
    border-top-right-radius: 12px;
    text-transform: uppercase;
    color: var(--panel-2);
    background: var(--fg);
    font-weight: 700;
    line-height: 1.1;
}
.game-clock-max-text {
    font-size: 14px;
    flex-grow: 1;
    line-height: 1;
    font-weight: 700;
    white-space: nowrap;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.game-clock-blinds .sep {
    color: var(--muted);
}
/* The K suffix on thousands-blinds renders smaller and lifted toward
   the cap line so the eye lands on the digits first. em-sized so it
   tracks whatever font-size the fitter just settled on. */
.game-clock-blinds .bk {
    font-size: 0.55em;
    vertical-align: 0.55em;
    margin-left: 0.05em;
    color: var(--muted);
}
/* 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; }
/* Small "Next: X / Y" or "Next: 20m Break" line under the time. */
.game-clock-next {
    font-size: clamp(22px, 3.5vw, 36px);
    color: var(--muted);
}
.game-clock-next:empty { display: none; }
/* Pre-game state: no level to count toward yet, so the "Next:" tease
   stays hidden until cards are up. */
.game-clock--pre-game .game-clock-next { display: none; }
.game-clock-pause {
    margin-top: 14px;
    margin-bottom: 14px;
}
/* The form is always rendered for admins (server can't time-gate it
   correctly in preview mode); JS unhides it once the apparent clock
   crosses scheduled_at by adding .is-cards-up to the root. */
.game-clock:not(.is-cards-up) .game-clock-pause { display: none; }
.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%; }

