html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

h1:focus {
    outline: none;
}

a, .btn-link {
    color: #0071c1;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

    .loading-progress circle {
        fill: none;
        stroke: #e0e0e0;
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: #1b6ec2;
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }

code {
    color: #c02d76;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ── Kiosk rotation container & corner logo ──────────────────────────────── */

.kiosk-rotation-container {
    position: relative;
}

/* Config-driven orientation overrides: these take precedence over @media queries
   so the admin-configured orientation is authoritative for paired devices.
   Standalone pages (no .kiosk-rotation-container) fall back to viewport media queries. */
.kiosk-rotation-container .kiosk-promo-levels {
    grid-template-columns: 1fr 1fr;
    column-gap: 3rem;
}
.kiosk-rotation-container.kiosk-portrait .kiosk-promo-levels {
    grid-template-columns: 1fr;
    column-gap: 0;
}

.kiosk-rotation-container .kiosk-promo-tiers {
    grid-template-columns: 1fr 1fr;
    column-gap: 3rem;
}
.kiosk-rotation-container.kiosk-portrait .kiosk-promo-tiers {
    grid-template-columns: 1fr;
    column-gap: 0;
}

.kiosk-rotation-container .kiosk-promo-value-hero .kiosk-hero-amount,
.kiosk-rotation-container .kiosk-promo-value-hero .kiosk-hero-time {
    font-size: 12vw;
}
.kiosk-rotation-container.kiosk-portrait .kiosk-promo-value-hero .kiosk-hero-amount,
.kiosk-rotation-container.kiosk-portrait .kiosk-promo-value-hero .kiosk-hero-time {
    font-size: 16vw;
}

/* ── Kiosk Waitlist Board ─────────────────────────────────────────────────── */

.kiosk-waitlist-header-logo {
    height: 2em;
    max-width: 40%;
    object-fit: contain;
    flex-shrink: 0;
}

.kiosk-board {
    display: flex;
    flex-wrap: nowrap;
    height: calc(100vh - var(--kiosk-chrome));
    overflow: hidden;
}

.kiosk-board-column {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--mud-palette-lines-default);
    overflow: hidden;
}

.kiosk-board-column:last-child {
    border-right: none;
}

.kiosk-board-scroll {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

.kiosk-board-table td,
.kiosk-board-table th {
    font-size: 1.05em;
    padding-top: 0.6em;
    padding-bottom: 0.6em;
}

.kiosk-stale-row {
    opacity: 0.65;
}

.kiosk-checked-in {
    color: var(--mud-palette-success);
    white-space: nowrap;
}

.kiosk-not-checked-in {
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

.kiosk-board-center {
    display: flex;
    align-items: center;
    justify-content: center;
    height: calc(100vh - var(--kiosk-chrome));
}

.kiosk-board-center-column {
    flex-direction: column;
    gap: 0.5em;
}

/* Fit-to-fill: waitlist board scales to fill available space without clipping.
   --kiosk-rows (longest column entry count + header), --kiosk-cols,
   --kiosk-name-chars, --kiosk-status-chars, and --kiosk-header-chars are
   emitted by Blazor on the board wrapper. Takes the tighter of vertical and
   horizontal fit, saturates at the fit point. The horizontal divisor uses the
   wider of row width — player name plus the check-in Status column, at 1.05em
   body — and header-title width (at 1.25em h6). */
.kiosk-rotation-container .kiosk-board {
    font-size: max(1.05rem, calc(
        min(
            (100vh - var(--kiosk-chrome)) / (5 + var(--kiosk-rows, 1) * 2.4),
            100vw / var(--kiosk-cols, 1) / (4 + max((var(--kiosk-name-chars, 12) + var(--kiosk-status-chars, 0)) * 0.6, var(--kiosk-header-chars, 12) * 0.75))
        )
    ));
}

/* ── Kiosk promo board (dark treatment) ─────────────────────────────────── */

.kiosk-promo-dark {
    background: #121212;
    color: #f5f5f5;
    min-height: calc(100vh - var(--kiosk-chrome));
}

/* ADR-0188: logo band on top, title band under it. The two never share a line, so
   a headline long enough to fill the board's width cannot ride over the mark. */
.kiosk-promo-header {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 1.5em 1em 0.5em;
}

.kiosk-header-banner {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    /* In title units, not em, so the gap tracks the operator's text size rather than
       the ladder's fit-to-fill root — and stays the constant the fit below reserves. */
    margin-bottom: calc(var(--kiosk-title-size, 2.125rem) * 0.25);
}

.kiosk-header-logo {
    /* ADR-0186: keyed to the title, not the ladder root — a 20-tier board would
       otherwise pair a 60px logo with a 120px headline. 1.23 preserves the ratio the
       old 2.62em-of-root sizing produced against a 2.125em-of-root title.

       ADR-0188: the banner is the full board width, so the title-keyed width cap
       ADR-0187 added is gone — height is the only binding constraint and a wide mark
       runs as wide as it needs instead of letterboxing. 100% keeps it inside the board. */
    height: calc(var(--kiosk-title-size, 2.125rem) * 1.23);
    max-width: 100%;
    object-fit: contain;
}

.kiosk-promo-title {
    color: #f5f5f5 !important;
    font-weight: 700;
    /* ADR-0186: an absolute size the operator sets, not derived from the board's
       fit-to-fill root. The board picks name vs headline via --kiosk-title-size.
       !important beats MudBlazor's own .mud-typography-h4. */
    font-size: var(--kiosk-title-size, 2.125rem) !important;
}

.kiosk-promo-levels {
    display: grid;
    gap: 0;
    padding: 0.5em 2em 2em;
}

@media (orientation: landscape) {
    .kiosk-promo-levels {
        grid-template-columns: 1fr 1fr;
        column-gap: 3rem;
    }
}

@media (orientation: portrait) {
    .kiosk-promo-levels {
        grid-template-columns: 1fr;
    }
}

.kiosk-promo-level-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.5em 0.75em;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    font-size: 1.4em;
}

.kiosk-promo-level-time {
    font-weight: 500;
}

.kiosk-promo-level-value {
    font-weight: 700;
    font-size: 1.07em;
}

/* ── Kiosk promo board – Progressive tier ladder ─────────────────────────── */

.kiosk-promo-tiers {
    display: grid;
    gap: 0;
    padding: 0.5em 2em 2em;
}

@media (orientation: landscape) {
    .kiosk-promo-tiers {
        grid-template-columns: 1fr 1fr;
        column-gap: 3rem;
    }
}

@media (orientation: portrait) {
    .kiosk-promo-tiers {
        grid-template-columns: 1fr;
    }
}

.kiosk-promo-tier-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.5em 0.75em;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    font-size: 1.4em;
}

.kiosk-promo-tier-position {
    font-weight: 500;
}

.kiosk-promo-tier-value {
    font-weight: 700;
    font-size: 1.07em;
}

.kiosk-row-past {
    opacity: 0.4;
    text-decoration: line-through;
}

.kiosk-row-next {
    font-weight: 700;
    border: 2px solid var(--cm-promo-highlight, var(--mud-palette-primary));
    border-radius: 6px;
    background: rgba(255,255,255,0.05);
}

.kiosk-promo-value-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    flex: 1;
    padding: 2rem;
}

.kiosk-promo-value-hero .kiosk-hero-amount {
    font-weight: 800;
    line-height: 1;
    color: var(--cm-promo-highlight, var(--mud-palette-primary));
}

.kiosk-promo-value-hero .kiosk-hero-basis {
    font-size: 2em;
    font-weight: 500;
    opacity: 0.7;
    margin-top: 0.5rem;
}

/* The Current Level Board's draw time: read at the same weight as the dollar amount
   beside it, not as a dimmed caption. Shares the amount's font-size rule below
   rather than kiosk-hero-basis, which is sized and margined for a caption
   underneath the amount. */
.kiosk-promo-value-hero .kiosk-hero-time {
    font-weight: 500;
}

@media (orientation: landscape) {
    .kiosk-promo-value-hero .kiosk-hero-amount,
    .kiosk-promo-value-hero .kiosk-hero-time {
        font-size: 12vw;
    }
}

@media (orientation: portrait) {
    .kiosk-promo-value-hero .kiosk-hero-amount,
    .kiosk-promo-value-hero .kiosk-hero-time {
        font-size: 16vw;
    }
}

/* ── Current Level Board hero (two stacked lines) ────────────────────────── */

/* The Current Level Board stacks a draw time above the amount, so its hero is two
   full-size lines where every other value board renders one. Sizing them from the
   viewport's width alone ignores the header above them — which is an absolute block
   the operator sets, up to 2.78x a 120px headline — so on a viewport that is wide
   relative to its height the pair pushed past the bottom of the board's own
   100vh box and the page's background showed through underneath it. Cap the pair at
   the height the header leaves: two line boxes at line-height 1, the hero's 2rem
   padding block, and the header's own 2rem. 2.15 leaves the glyphs a little room.
   The 1rem floor mirrors the ladder fits — a header tall enough to drive the
   remainder negative must not invalidate the whole declaration. */
.kiosk-promo-value-hero.kiosk-current-level-hero {
    --kiosk-hero-fit: max(1rem, calc(
        (100vh - var(--kiosk-chrome, var(--mud-appbar-height)) - var(--kiosk-header-block) - 6rem) / 2.15
    ));
    min-height: 0;
    overflow: hidden;
}

/* Both lines share the amount's tight line box so the fit above is exact. */
.kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-time {
    line-height: 1;
}

@media (orientation: landscape) {
    .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-amount,
    .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-time {
        font-size: min(12vw, var(--kiosk-hero-fit));
    }
}

@media (orientation: portrait) {
    .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-amount,
    .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-time {
        font-size: min(16vw, var(--kiosk-hero-fit));
    }
}

/* Config-driven orientation, same precedence rule as the rest of the board: the
   paired device's configured orientation beats the viewport media query. */
.kiosk-rotation-container .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-amount,
.kiosk-rotation-container .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-time {
    font-size: min(12vw, var(--kiosk-hero-fit));
}

.kiosk-rotation-container.kiosk-portrait .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-amount,
.kiosk-rotation-container.kiosk-portrait .kiosk-promo-value-hero.kiosk-current-level-hero .kiosk-hero-time {
    font-size: min(16vw, var(--kiosk-hero-fit));
}

/* The header block the ladder fit reserves, in title units. 1.3x is the title's own
   line box; a board carrying a logo adds the banner row above it — 1.23x for the mark
   plus the 0.25x gap under it (ADR-0188). Boards with no logo reserve neither. */
.kiosk-promo-dark {
    --kiosk-header-block: calc(var(--kiosk-title-size, 2.125rem) * 1.3);
}

.kiosk-promo-dark:has(.kiosk-header-banner) {
    --kiosk-header-block: calc(var(--kiosk-title-size, 2.125rem) * 2.78);
}

/* Fit-to-fill: ladder rows take whatever height the fixed header and footer leave
   (ADR-0186). The title and footnote are absolute pixel sizes the operator sets per
   board, so they come off the top as fixed blocks — --kiosk-header-block above,
   1.5x each footnote line — instead of being folded into the divisor. What
   remains is divided by the em-relative padding still in play (2em header + 2.5em
   ladder + 2em footer when present) plus 3.1em per row.

   --kiosk-rows, --kiosk-footnote-lines, --kiosk-footnote-pad, and --kiosk-title-size
   are emitted by Blazor on the board wrapper. Standalone pages emit no rows; the
   :has() scope and rotation-container selector keep them on plain fixed sizing. */
.kiosk-rotation-container .kiosk-promo-dark:has(.kiosk-promo-tiers),
.kiosk-rotation-container .kiosk-promo-dark:has(.kiosk-promo-levels) {
    font-size: max(1.05rem, calc(
        (100vh - var(--kiosk-chrome)
            - var(--kiosk-header-block)
            - var(--kiosk-footnote-lines, 0) * var(--kiosk-footer-size, 0.875rem) * 1.5)
        / (4.5 + var(--kiosk-rows, 1) * 3.1 + var(--kiosk-footnote-pad, 0))
    ));
}

.kiosk-rotation-container .kiosk-promo-footnote {
    overflow: hidden;
}

.kiosk-promo-footnote {
    text-align: center;
    padding: 0.5em 2em 1.5em;
    opacity: 0.7;
}

/* ADR-0186: every footnote line takes the operator's Footer size. Overrides the
   Typo.body2 rule the MudText inside carries. */
.kiosk-promo-footnote .mud-typography-body2 {
    font-size: var(--kiosk-footer-size, 0.875rem);
}

/* MudBlazor Typo override: rem → em so the waitlist board's column headers inherit
   from its fit-to-fill root. The promo title and footnote no longer need this — they
   carry the operator's absolute sizes (ADR-0186). */
.kiosk-rotation-container .mud-typography-h6 {
    font-size: 1.25em;
}

/* ── Room pinned header ───────────────────────────────────────────────────── */

/* Keeps the page title, banners, action buttons, and jump-to-table pills pinned
   below the fixed app bar while the table grid scrolls beneath them. */
.room-sticky-header {
    position: sticky;
    top: var(--mud-appbar-height);
    z-index: 1000; /* above the table grid, below the app bar (1100) and dialogs */
    background-color: var(--mud-palette-background);
    padding-top: 8px;
}

/* ── Club Settings pinned action bar ─────────────────────────────────────── */

/* Keeps Save/Discard reachable without scrolling back to the top of the long
   settings form, mirroring the Room Floor sticky-header treatment above. */
.club-settings-action-bar {
    position: sticky;
    top: var(--mud-appbar-height);
    z-index: 1000;
    background-color: var(--mud-palette-background);
    padding-top: 8px;
}

/* Same treatment for the player record page's Details tab. */
.player-record-action-bar {
    position: sticky;
    top: var(--mud-appbar-height);
    z-index: 1000;
    background-color: var(--mud-palette-background);
    padding-top: 8px;
}

/* ── Poker Table ──────────────────────────────────────────────────────────── */

.poker-table-wrapper {
    position: relative;
    width: 680px;
    height: 370px;
    margin: 0 auto;
    overflow: visible;
}

/* Horizontal racetrack (stadium): wide straights with fully-rounded ends
   (border-radius = half the height). Geometry is mirrored by SeatLayout.cs. */
.poker-table-felt {
    position: absolute;
    left: 60px;
    top: 70px;
    width: 560px;
    height: 220px;
    background: radial-gradient(ellipse at center, #2d7a2d 0%, #1a4d1a 100%);
    border-radius: 110px;
    border: 8px solid #5d3a1a;
    box-shadow: 0 4px 20px rgba(0,0,0,0.5);
    cursor: pointer;
}

.poker-dealer-marker {
    position: absolute;
    width: 80px;
    height: 80px;
    z-index: 1;
    pointer-events: none;
}

.poker-dealer-inner {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    display: flex;
    justify-content: center;
    align-items: center;
    background: #5d3a1a;
    border: 2px solid #8b6914;
    color: #f0d080;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    user-select: none;
}

.poker-seat {
    position: absolute;
    width: 96px;
    height: 96px;
    cursor: pointer;
    z-index: 1;
}

.poker-seat-inner {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 4px;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    user-select: none;
}

.poker-seat.seat-empty .poker-seat-inner {
    background: var(--cm-empty-seat-bg, #BDBDBD);
    border: 2px dashed rgba(255,255,255,0.4);
    color: var(--cm-empty-seat-text, rgba(255,255,255,0.7));
}

.poker-seat.seat-occupied .poker-seat-inner {
    background: var(--cm-occupied-seat-bg, #FFFFFF);
    border: 2px solid var(--mud-palette-primary);
    color: var(--cm-occupied-seat-text, var(--mud-palette-text-primary));
    box-shadow: 0 2px 6px rgba(0,0,0,0.3);
}

/* The seat whose detail drawer is open needs a channel nothing else on this element
   owns. `outline` is the low/negative-chip ring's (seat-ring-warning/error), and
   `border-color`/`box-shadow` on .poker-seat-inner belong to the drag states
   (seat-valid-target, drop-hover) — reusing any of them means whichever state's
   rule sits later in this file wins outright and the other reads as not selected.
   A badge layered on the seat itself, outside .poker-seat-inner, stays visible
   under a ring and independent of drag styling, and the check glyph carries the
   signal without relying on colour alone. */
.poker-seat.seat-selected::after {
    content: '\2713';
    position: absolute;
    top: -6px;
    right: -6px;
    width: 22px;
    height: 22px;
    line-height: 22px;
    text-align: center;
    border-radius: 50%;
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text, #FFFFFF);
    font-size: 13px;
    font-weight: 700;
    z-index: 2;
    pointer-events: none;
}

.poker-seat.seat-selectable:hover .poker-seat-inner {
    transform: scale(1.1);
    box-shadow: 0 3px 10px rgba(0,0,0,0.5);
}

.poker-seat-number {
    font-size: 10px;
    font-weight: bold;
    line-height: 1;
    margin-bottom: 2px;
    opacity: 0.7;
}

.cm-promo-amount {
    color: var(--cm-promo-highlight, var(--mud-palette-primary));
}

.tier-next {
    color: var(--cm-promo-highlight, var(--mud-palette-primary));
}

.poker-seat-player {
    font-size: 11px;
    font-weight: 600;
    line-height: 1.2;
    max-width: 86px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.poker-seat-membership {
    font-size: 9px;
    font-weight: 500;
    line-height: 1.1;
    margin-top: 1px;
    opacity: 0.75;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    max-width: 86px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.membership-tier-pill {
    display: inline-block;
    font-size: 8px;
    font-weight: 700;
    line-height: 1;
    padding: 2px 5px;
    border-radius: 6px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    max-width: 86px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.poker-seat-chips {
    font-size: 10px;
    font-weight: 700;
    line-height: 1.1;
    margin-top: 2px;
}

.seat-chips-negative {
    color: var(--mud-palette-error);
}

.seat-chips-low {
    color: var(--mud-palette-warning);
    font-size: 13px;
    font-weight: 800;
}

.seat-chips-empty {
    color: var(--mud-palette-text-disabled);
}

/* --- Club Settings hours: clock dialog sizing ------------------------------
   The weekly-hours tables use narrow time-picker inputs. Setting that width on
   the MudTimePicker root also constrained its dialog-variant clock popup, so
   the clock opened very small with a horizontal scrollbar. Apply the narrow
   width to the input only, and guarantee the clock dialog renders the full
   clock face (260px) without horizontal overflow. The :has() guard scopes the
   dialog rule to time pickers (those containing a clock) so date pickers are
   unaffected. */
.club-hours-timepicker .mud-input-control {
    width: 110px;
}

.mud-picker-content:has(.mud-picker-time-clock) {
    min-width: 328px;
    overflow-x: hidden;
}

.poker-seat.seat-drag-source {
    opacity: 0.4;
}

.poker-seat.seat-valid-target .poker-seat-inner {
    box-shadow: 0 0 8px 3px rgba(76, 175, 80, 0.6);
    border-color: #4CAF50;
}

/* Low/negative chip seat ring (club toggle) — an outline layered over the seat so it
   composes with any club-configured seat background rather than replacing it. */
.poker-seat.seat-ring-warning .poker-seat-inner {
    outline: 3px solid var(--mud-palette-warning);
    outline-offset: 2px;
}

.poker-seat.seat-ring-error .poker-seat-inner {
    outline: 3px solid var(--mud-palette-error);
    outline-offset: 2px;
}

.poker-seat.drop-hover .poker-seat-inner {
    box-shadow: 0 0 12px 5px rgba(76, 175, 80, 0.9);
    border-color: #4CAF50;
    transform: scale(1.1);
}

.poker-seat-label {
    font-size: 10px;
    opacity: 0.6;
    line-height: 1;
}

/* ── Detail Drawer ────────────────────────────────────────────────────────── */

/*
 * MudBlazor colours list-item icons with --mud-palette-action-default (a dark
 * rgba tuned for its default white drawer). Our drawer backgrounds are dark, so
 * the icons all but disappear. Inherit the drawer's text colour instead, which
 * the theme already derives from the drawer background.
 */
.mud-drawer .mud-list-item-icon {
    color: inherit;
}

/* ── Selected list rows ───────────────────────────────────────────────────── */

/*
 * MudBlazor 9 barely paints a selected list row. `.mud-selected-item` has no rule
 * of its own — it appears in the stylesheet only as a `:not()` guard — so a
 * hand-rolled `Class="mud-selected-item"` paints nothing at all, and managed
 * MudList selection adds only `--mud-palette-primary-hover`, which is the primary
 * colour at ~6% opacity. Down a stack of near-identical options — chip tiers,
 * destination tables, promo rungs — that tint is easy to miss, and picking the
 * wrong one is a cash mistake.
 *
 * Paint it properly, and paint it identically however it was selected: a
 * primary-tinted fill, a solid left accent bar, and heavier text. The bar and the
 * weight carry the signal without relying on colour, so the row still reads as
 * selected in monochrome. Everything derives from --mud-palette-primary, so it
 * follows whatever primary the club's theme sets rather than pinning a hex.
 *
 * The `!important`s are not decoration. MudBlazor's own `.mud-primary-hover` sets
 * the background with `!important` and `.mud-primary-text` the colour, and among
 * `!important` declarations specificity decides — `.mud-list-item.mud-selected-item`
 * (two classes) outranks either. Overriding the colour is what keeps a managed
 * selection from reading differently than a hand-rolled one, and keeps the text
 * off a same-hue tint of itself.
 */
.mud-list-item.mud-selected-item {
    background-color: rgba(var(--mud-palette-primary-rgb), 0.18) !important;
    box-shadow: inset 4px 0 0 0 var(--mud-palette-primary);
    color: var(--mud-palette-text-primary) !important;
    font-weight: 600;
}

.mud-list-item.mud-selected-item:hover {
    background-color: rgba(var(--mud-palette-primary-rgb), 0.26) !important;
}

/* ── QR flyers ────────────────────────────────────────────────────────────── */

/*
 * Every QR flyer variant (registration, member, ...) has its own route on the
 * empty layout, so printing it prints only the flyer. Sizes are in inches/points
 * so the artwork fills one letter page regardless of the browser's
 * screen-to-print scaling.
 */

.qr-flyer-actions {
    padding: 1rem;
    text-align: center;
}

.qr-flyer {
    box-sizing: border-box;
    width: 8.5in;
    height: 11in;
    max-width: 100%;
    margin: 0 auto;
    padding: 0.5in;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: #fff;
    color: #000;
}

/*
 * Full-bleed to the physical page edge, undoing .qr-flyer's own padding, so it
 * reads as a band across the sheet rather than an inset banner.
 */
.qr-flyer-band {
    box-sizing: border-box;
    width: calc(100% + 1in);
    margin: -0.5in -0.5in 0.3in;
    padding: 0.2in 0;
    font-size: 28pt;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/*
 * Fill vs outline, not two colors: a venue printing in monochrome collapses
 * any two colors into the same gray, but a solid fill next to an empty
 * outline stays distinct.
 */
.qr-flyer-band-solid {
    background: #000;
    color: #fff;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
}

.qr-flyer-band-outlined {
    background: none;
    border: 3pt solid #000;
    color: #000;
}

.qr-flyer-name {
    margin: 0 0 0.3in;
    font-size: 48pt;
    line-height: 1.1;
    font-weight: 700;
    max-width: 100%;
    overflow-wrap: break-word;
}

.qr-flyer-logo {
    max-height: 2in;
    max-width: 6in;
    object-fit: contain;
    margin-bottom: 0.35in;
}

.qr-flyer-qr {
    width: 4.5in;
    height: 4.5in;
    max-width: 100%;
    object-fit: contain;
    /* Keep the QR modules square-edged when the 250px PNG is scaled up. */
    image-rendering: pixelated;
}

.qr-flyer-instruction {
    margin: 0.3in 0 0;
    font-size: 24pt;
    font-weight: 500;
}

.qr-flyer-fallback {
    margin: 0.1in 0 0;
    font-size: 16pt;
}

@media print {
    @page {
        size: letter portrait;
        margin: 0;
    }

    html, body {
        margin: 0;
        padding: 0;
    }

    .qr-flyer-actions {
        display: none !important;
    }

    .qr-flyer {
        width: 100%;
        height: 100vh;
        margin: 0;
        /* A page box rounded up by a fraction spills into a blank second sheet. */
        overflow: hidden;
        page-break-after: avoid;
        break-after: avoid;
    }
}

.mud-charts-gridlines-yaxis,
.mud-charts-gridlines-xaxis-lines {
    stroke-dasharray: 4 4;
}

.promo-row-inactive {
    opacity: 0.6;
}

/* Floor staff are plausibly on phones/tablets, so the app bar's environment badge
   shortens its wording under a narrow app bar rather than ever being hidden. */
.environment-badge-short {
    display: none;
}

.empty-layout-environment-bar {
    display: flex;
    justify-content: flex-end;
    padding: 8px 16px 0;
}

/* Same shortening rule for the Room Floor heading's environment suffix — a long
   club name plus the full suffix would wrap the sticky header on a phone. */
.room-heading-environment-short {
    display: none;
}

@media (max-width: 600px) {
    .environment-badge-full {
        display: none;
    }

    .environment-badge-short {
        display: inline;
    }

    .room-heading-environment-full {
        display: none;
    }

    .room-heading-environment-short {
        display: inline;
    }
}

/* Report component dedup sweep (#2680). Shared by ReportTotalRow — replaces five bespoke
   "font-weight:bold;border-top:1px or 2px solid" total-row classes (one used 1px; normalized
   to 2px here to match the other four). */
.report-total-row {
    font-weight: bold;
    border-top: 2px solid;
}

/* Shared by every hand-written report table's numeric columns — replaces per-cell
   `style="text-align:right"` so alignment is consistent across all report panels, including
   the ones that previously didn't right-align numeric columns at all. */
.report-numeric-cell {
    text-align: right;
}

/* Player Table Time: click-to-sort column headers (#3128). The header itself carries the
   pointer cursor; the small arrow icon inside indicates direction only on the active column. */
.ptt-sort-header {
    user-select: none;
}

.ptt-sort-icon {
    vertical-align: middle;
    margin-left: 2px;
}

/* Rank stays pinned to Table Time regardless of the active sort (ADR-0233) — when some other
   column is driving the visible order, Rank de-emphasises to signal that the row order in front
   of the reader is no longer the order Rank was computed from. */
.ptt-rank-deemphasized {
    color: var(--mud-palette-text-secondary);
    opacity: 0.65;
}

/* Actual Hours: the clock window that sits under each duration figure. Muted and smaller so the
   duration stays the scannable primary value and the window reads as the context you drop to
   when a row looks wrong. */
.ah-window {
    font-size: 0.85em;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

/* Actual Hours, Day of Week grouping: Min/Max are the first columns dropped on a narrow
   viewport, keeping Business Days, Posted, and Avg Floor Hours visible. */
@media (max-width: 600px) {
    .ah-dow-hide-narrow {
        display: none;
    }
}

/* Shared by every Corrections row action button (Void, Void & Reissue, Reverse, Edit) —
   replaces per-button `style="min-width:140px"` so every grid's action column lines up the
   same way regardless of which verb it renders. */
.corrections-action-button {
    min-width: 140px;
}
