:root {
    /* Single source of truth for gender-based name/initials colours. Used by the
       Members tab (name cells + photo-fallback initials) and the live session
       Attendee Stats names so the two never drift. CSS custom properties inherit
       through the cascade, so Blazor scoped component CSS (PegboardApp.styles.css)
       can read these despite the scoping attribute. */
    --gender-male: #2e6fa9;
    --gender-female: #a85283;
}

html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    width: 100%;
    min-height: 100vh;
    margin: 0;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

/* #268: Bootstrap's default .btn-outline-secondary is grey ink + grey border on
   a transparent fill, which reads as a disabled control in our UI - reported on
   End session / Kiosk (full screen) but the washed-out look is shared by every
   outline-secondary button, so fix it at the source. Give it a tangible surface
   and darker ink so it's obviously an active button; genuinely-disabled buttons
   still fade out via Bootstrap's .65 :disabled opacity, restoring the contrast
   between enabled and disabled. */
.btn-outline-secondary {
    color: #2d3540;
    border-color: #97a0aa;
    background-color: #eef1f4;
}

.btn-outline-secondary:hover,
.btn-outline-secondary:focus-visible,
.btn-outline-secondary:active {
    color: #fff;
    background-color: #5a636d;
    border-color: #545d66;
}

.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;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.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."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.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;
}

@media (min-width: 641px) {
    /* In fullscreen the sidebar is hidden by default but stays in the DOM so
       the hamburger overlay (BL-008) can slide it in on demand. The .page--kiosk
       overlay rules in MainLayout.razor.css already handle the open/closed state. */
    body.clubSession-dashboard-fullscreen .content {
        padding-left: 0.35rem !important;
        padding-right: 0.35rem !important;
    }
}

/* BL-008 / #276 / #298: the top bar (hamburger + any status banner) flows
   normally in fullscreen, the same as in every other mode. It used to float as
   an absolute 0-height layer to reclaim a row, but since #298 the status banner
   shares that bar - floating it overlaid the dashboard's own kiosk header
   (hamburger over the session title, banner over the controls; screenshot
   2026-06-09). A normal-flow row sits cleanly above the dashboard header with
   nothing overlapping and the banner still clickable; the profile menu is
   reachable via the hamburger-opened sidebar footer. So: no special fullscreen
   positioning for the top bar. */
body.clubSession-dashboard-fullscreen .clubSession-dashboard-shell {
    min-height: 100vh;
}

.legal-page {
    max-width: 48rem;
    margin: 0 auto;
    padding: 1.5rem 1rem 3rem;
    color: #2a3a4d;
    line-height: 1.55;
}

.legal-page h1 {
    color: #1f3f5e;
    margin: 0 0 0.35rem;
}

.legal-page h2 {
    color: #1f3f5e;
    margin: 1.4rem 0 0.4rem;
    font-size: 1.15rem;
}

.legal-page p,
.legal-page ul {
    margin: 0 0 0.75rem;
}

.legal-page ul {
    padding-left: 1.25rem;
}

.legal-page .legal-meta {
    background: #f6f9fc;
    border: 1px solid #c4d1de;
    border-radius: 0.5rem;
    padding: 0.5rem 0.7rem;
    font-size: 0.9rem;
    color: #4d6177;
}

/* Shared auth-page layout — used by /login, /signup, /forgot-password, /reset-password, /confirm-email.
   Previously scoped to Login.razor.css, which meant the other auth pages rendered unstyled
   (inputs and buttons collapsed against each other). */
/* Shared form markers used across auth + admin forms. */
.required-marker {
    color: #b34040;
    margin-left: 0.1rem;
}

.required-legend {
    margin: 0.4rem 0 0;
    color: #6c7a8c;
    font-size: 0.8rem;
}

.field-hint {
    color: #6c7a8c;
    font-weight: 400;
    font-size: 0.8rem;
    margin-left: 0.25rem;
}

.login-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Top padding 3rem → 1rem so the brand block sits closer to the top
       and the auth card stays in the visible viewport on tablet portrait. */
    padding: 1rem 1.5rem 2rem;
    background:
        radial-gradient(circle at 28% 18%, rgba(140, 190, 240, 0.22) 0%, transparent 55%),
        radial-gradient(circle at 78% 88%, rgba(93, 176, 97, 0.10) 0%, transparent 55%),
        linear-gradient(180deg, #294f76 0%, #1f3d5b 100%);
    margin: -1rem -1.5rem -1rem;
}

/* MainLayout's .content padding goes asymmetric at >=1280px (2rem left,
   1.5rem right). Match that on the left so the gradient bleeds flush. */
@media (min-width: 1280px) {
    .login-shell {
        margin-left: -2rem;
    }
}

.login-brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.2rem;
    margin-bottom: 0.75rem;
}

.login-brand-mark {
    font-size: 2.4rem;
    font-weight: 800;
    color: #ffffff;
    letter-spacing: 0.6px;
    line-height: 1;
    text-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
}

/* BL-057 (2026-04-26 v6): split assets — figures + wordmark sized
   independently. Mike: "people in the logo are too large vs the text".
   Figures shrunk relative to wordmark; on tablet portrait the form now
   stays in view. Both PNGs are transparent so they sit natively on the
   navy gradient. */
.login-brand-logo {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

.login-brand-logo .login-brand-figures {
    display: block;
    height: 3.5rem;
    width: auto;
}

.login-brand-logo .login-brand-wordmark {
    display: block;
    height: 3.5rem;
    width: auto;
    max-width: 100%;
}

.login-brand-tagline {
    color: #cfe1f3;
    font-size: 0.95rem;
    font-weight: 500;
    letter-spacing: 0.2px;
}

.login-card {
    width: min(30rem, 92vw);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 0.85rem;
    background: #ffffff;
    padding: 1.5rem 1.5rem 1.3rem;
    box-shadow: 0 14px 32px rgba(8, 24, 50, 0.34);
}

.login-card h1 {
    margin: 0 0 0.35rem;
    color: #1f3f5e;
}

.login-card .form-control {
    margin-bottom: 0.5rem;
}

.login-help {
    color: #5e748a;
    margin: 0 0 0.8rem;
}

.login-actions {
    margin-top: 1.0rem;
}

.login-signup-link {
    margin-top: 0.75rem;
    color: #4d6177;
    font-size: 0.92rem;
}

.login-signup-link a,
.login-forgot-link a {
    color: #0f4f8b;
    font-weight: 600;
}

.login-forgot-link {
    margin-top: 0.35rem;
    color: #4d6177;
    font-size: 0.9rem;
}

.login-demo-help {
    margin: 0;
    color: #5f7083;
    font-size: 0.85rem;
}

/* #683 (portal S9a): the "email me a code" passwordless block below the password form. */
.login-emailcode {
    margin-top: 0.25rem;
}

.login-emailcode .login-help {
    margin-bottom: 0.5rem;
}

/* #683: off-screen honeypot on the login page (Login.razor reuses the name but the
   Signup scoped style doesn't reach here — provide a global visually-hidden style). */
.login-emailcode .signup-website-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Top-level honeypot hider: the .signup-website-field field must be off-screen ANYWHERE
   the class is used — /forgot-password (ForgotPassword.razor) renders it with no
   .login-emailcode wrapper, so the scoped rule above never reached it and the "Club
   website" honeypot showed (a visible field that real users can fill, rejecting genuine
   requests). This unscoped rule covers every host of the class. */
.signup-website-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.login-error {
    margin-top: 0.75rem;
    border: 1px solid #d88f8f;
    background: #f7e3e3;
    color: #8a2d2d;
    border-radius: 0.5rem;
    padding: 0.35rem 0.5rem;
    font-size: 0.88rem;
}

.login-notice {
    margin-top: 0.75rem;
    border: 1px solid #9ec3e6;
    background: #eaf3fc;
    color: #1c4b78;
    border-radius: 0.5rem;
    padding: 0.35rem 0.5rem;
    font-size: 0.88rem;
}

.login-readonly-field {
    color: #4d6177;
    background: #eef3f8;
}

.legal-page a {
    color: #0f4f8b;
    font-weight: 600;
}

/* MCP track 3 — kiosk PIN UI (settings card + unlock modal + sidebar indicator) */

.kiosk-pin-card {
    border-color: #d7e0ec;
}

.kiosk-pin-status {
    color: #4e6f8e;
    margin-bottom: 0.5rem;
}

.kiosk-pin-form .form-label {
    font-size: 0.85rem;
    font-weight: 600;
    color: #24384f;
    margin-bottom: 0.2rem;
}

.kiosk-pin-form input.form-control {
    max-width: 16rem;
    letter-spacing: 0.4em;
    font-family: monospace;
    font-size: 1.05rem;
}

.kiosk-unlock-modal {
    position: fixed;
    inset: 0;
    z-index: 1090;
    display: flex;
    align-items: center;
    justify-content: center;
}

.kiosk-unlock-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(13, 44, 85, 0.45);
}

.kiosk-unlock-dialog {
    position: relative;
    background: #ffffff;
    border-radius: 0.5rem;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
    padding: 1.25rem 1.4rem;
    max-width: 22rem;
    width: 92%;
}

.kiosk-unlock-dialog h3 {
    font-size: 1.05rem;
    margin-bottom: 0.4rem;
    color: #0d2c55;
}

/* Effective Permissions (Kiosk Axis 2, epic #325) — S2b-i β: the exit-PIN keypad (Screen B).
   Ported verbatim from the v2 archive. Masked-dot display + big courtside touch keypad. */
.kiosk-exit-pin-dialog {
    text-align: center;
    max-width: 20rem;
    position: relative; /* anchor the × close (UX-3) */
}

/* UX-3: the visible × close, top-right, so a mis-tap has an obvious escape (→ Cancel, stays locked). */
.kiosk-exit-pin-close {
    position: absolute;
    top: 0.6rem;
    right: 0.7rem;
}

/* UX-10: reserve the wrong-PIN error slot so the keypad never jumps when the error appears/clears. */
.kiosk-exit-pin-error-slot {
    min-height: 2rem;
    margin-top: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.kiosk-pin-dots {
    display: flex;
    justify-content: center;
    gap: 0.6rem;
    margin: 0.9rem 0 0.4rem;
    min-height: 1rem;
}

.kiosk-pin-dot {
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 999px;
    border: 2px solid #0d2c55;
    background: transparent;
}

.kiosk-pin-dot.is-filled {
    background: #0d2c55;
}

.kiosk-keypad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
    margin-top: 0.8rem;
}

.kiosk-keypad-key {
    min-height: 3.75rem; /* ~64px courtside touch floor */
    font-size: 1.4rem;
    font-weight: 600;
    color: #0d2c55;
    background: #f1f5f9;
    border: 1px solid #cbd5e1;
    border-radius: 0.5rem;
}

.kiosk-keypad-key:hover:not(:disabled) {
    background: #e2e8f0;
}

.kiosk-keypad-key:disabled {
    opacity: 0.5;
}

.kiosk-keypad-confirm {
    color: #ffffff;
    background: #0d6efd;
    border-color: #0d6efd;
    font-size: 1.15rem; /* "OK" text (UX-2) reads better a touch smaller than the 1.4rem digits */
}

.kiosk-keypad-confirm:hover:not(:disabled) {
    background: #0b5ed7;
}

/* UX-3: Cancel bumped to keypad-key weight (min-height inherited from .kiosk-keypad-key) but
   full-width + smaller type so it reads as the calm secondary, not a 4th keypad column. */
.kiosk-exit-pin-cancel.kiosk-keypad-key {
    width: 100%;
    font-size: 1rem;
    background: transparent;
}

.kiosk-keypad-del {
    font-size: 1.2rem;
}

/* ADR 0007 (#985 E4): CoordinatorApproval approve-to-start modal. Reuses the kiosk keypad
   classes above for the PIN entry; these style the violations list + centring. */
.prepared-approve-lead {
    margin: 0 0 0.4rem;
    font-size: 0.9rem;
}

.prepared-approve-violations {
    margin: 0 0 0.75rem;
    padding-left: 1.1rem;
    font-weight: 600;
    color: #8a3213;
}

.prepared-approve-content .kiosk-keypad,
.prepared-approve-content .kiosk-pin-dots {
    margin-left: auto;
    margin-right: auto;
}

.prepared-approve-error-slot {
    min-height: 1.75rem;
    margin: 0.4rem 0;
}

/* Shared password / PIN length-hint styles. Used by /signup, /reset-password,
   and the kiosk PIN setup card so the live feedback pattern is consistent. */
.signup-password-hint {
    font-size: 0.82rem;
    margin-bottom: 0;
}

.signup-password-hint-neutral {
    color: #6c757d;
}

.signup-password-hint-short {
    color: #8a5a00;
}

.signup-password-hint-ok {
    color: #1e7e34;
}

/* Tile-grid bulk session check-in (#194) */
.bulk-check-in-hint {
    color: #4a5870;
    font-size: 0.88rem;
    margin: 0 0 0.6rem 0;
}
/* Single-tap A-Z letter filter for the bulk-add modal (#200) */
.bulk-check-in-letters-special {
    display: flex;
    gap: 0.3rem;
    margin: 0 0 0.4rem 0;
}
/* 26 letters across 13 columns -> exactly two full rows. */
.bulk-check-in-letters {
    display: grid;
    grid-template-columns: repeat(13, 1fr);
    gap: 0.3rem;
    margin: 0 0 0.7rem 0;
}
.bulk-check-in-letter {
    /* #909 touch-target polish: >= ~30px tappable box (1.9rem at a 16px root), content
       centred. inline-flex so the min-height applies inside the grid cells too. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    min-width: 1.9rem;
    min-height: 1.9rem;
    padding: 0.5rem 0.35rem;
    border: 1px solid #c5cfde;
    border-radius: 6px;
    background: #fff;
    color: #1f3f5e;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background 80ms ease, border-color 80ms ease, color 80ms ease;
}
.bulk-check-in-letters-special .bulk-check-in-letter {
    padding-left: 0.9rem;
    padding-right: 0.9rem;
}
.bulk-check-in-letter:hover:not(:disabled) {
    border-color: #2962ff;
    background: #f4f8ff;
}
.bulk-check-in-letter.active {
    background: #2962ff;
    border-color: #2962ff;
    color: #fff;
}
.bulk-check-in-letter:disabled {
    opacity: 0.3;
    cursor: default;
}
.bulk-check-in-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr));
    grid-auto-rows: min-content;
    gap: 0.5rem;
    /* FIXED height (not min/max) so the pane is exactly the same size whether the
       filter shows 1 tile or 50 - it never shrinks/shifts when you pick a letter
       (#200 feedback). Tiles top-align; the list scrolls inside when long. */
    height: 50vh;
    overflow-y: auto;
    align-content: start;
}
.bulk-check-in-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 0.5rem 0.3rem;
    border: 1px solid #c5cfde;
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
    position: relative;
    text-align: center;
    transition: border-color 80ms ease, background 80ms ease, box-shadow 80ms ease;
}
/* Tone each tile by gender, matching the waiting-queue chips (blue = men, pink =
   women). The gender tone is KEPT through hover AND selection — interaction is shown
   with a shadow / ring (+ the check glyph), NOT by replacing the gender background,
   which would wash every tile blue regardless of gender. */
.bulk-check-in-tile.gender-male {
    background: #dfeeff;
    border-color: #8fb6e0;
}
.bulk-check-in-tile.gender-female {
    background: #ffe4f3;
    border-color: #e3a4c7;
}
.bulk-check-in-tile:hover {
    box-shadow: 0 2px 6px rgba(13, 44, 85, 0.18);
}
.bulk-check-in-tile.selected {
    box-shadow: inset 0 0 0 2px #2962ff;
}
.bulk-check-in-tile:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}
.bulk-check-in-tile-photo {
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    object-fit: cover;
    background: #e6ebf3;
}
.bulk-check-in-tile-placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 1rem;
    color: #4a5870;
}
.bulk-check-in-tile-name {
    font-size: 0.78rem;
    line-height: 1.1;
    color: #28344a;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    word-break: break-word;
}
.bulk-check-in-tile-check {
    position: absolute;
    top: 0.2rem;
    right: 0.3rem;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 50%;
    background: #2962ff;
    color: #fff;
    font-size: 0.72rem;
    line-height: 1.1rem;
    text-align: center;
}

/* CSV import self-service panel on Members (#95) */
.member-import-panel {
    border: 1px solid #c5cfde;
    border-radius: 8px;
    background: #f8fafc;
    padding: 1rem 1.2rem;
    margin: 0.5rem 0 1rem 0;
}
.member-import-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
.member-import-input-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0.5rem 0;
    flex-wrap: wrap;
}
.member-import-file-btn {
    position: relative;
    overflow: hidden;
    cursor: pointer;
    margin: 0;
}
.member-import-file-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
    width: 100%;
    height: 100%;
}
.member-import-file-name {
    font-size: 0.85rem;
    color: #4a5870;
    word-break: break-all;
}
.member-import-textarea {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.85rem;
}
.member-import-summary {
    font-size: 0.9rem;
}
.member-import-preview {
    max-height: 22rem;
    overflow-y: auto;
}

/* #393: the floating "Feedback" button (.quick-feedback-fab, position:fixed bottom-right,
   z-index 1900) sat on top of the score-entry popover and its Save control, obscuring score
   entry during a live session. Hide it whenever an overlay that the operator is actively
   working in is open - the score-entry popover (which renders .score-combo-backdrop) or any
   Bootstrap-style modal (.modal.show). The Feedback button reappears once the overlay closes. */
body:has(.score-combo-backdrop) .quick-feedback-fab,
body:has(.modal.show) .quick-feedback-fab {
    display: none !important;
}

/* #822: unsupported / out-of-date browser warning. The banner is always server-rendered
   (in App.razor, static SSR) so it exists even when the interactive runtime never starts,
   but is hidden until <html> gets the .browser-unsupported class from the feature-detect
   preflight or the liveness fallback. Pinned to the TOP so an old-device user sees it at
   once (unlike #blazor-error-ui which sits at the bottom for a post-start error), and above
   every overlay so it is never obscured. Mirrors the lightyellow warning convention of
   #blazor-error-ui. */
#unsupported-browser-ui {
    display: none;
}

html.browser-unsupported #unsupported-browser-ui {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    box-sizing: border-box;
    padding: 0.7rem 1.25rem;
    background: lightyellow;
    color: #2d3540;
    color-scheme: light only;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    font-size: 0.95rem;
    line-height: 1.4;
    /* Above every overlay (modal backdrops ~1040, score popover 1200, feedback FAB 1900,
       and #blazor-error-ui uses the max) so the warning and its Reload link are always visible. */
    z-index: 2147483646;
}

#unsupported-browser-ui .unsupported-browser-text {
    flex: 1 1 16rem;
    min-width: 0;
}

#unsupported-browser-ui .unsupported-browser-reload {
    flex: 0 0 auto;
    font-weight: 600;
    color: #1861ac;
    text-decoration: underline;
}

/* ── Effective Permissions (Kiosk Axis 2, epic #325) — Locked-visible affordance ──
   Owner feedback (2026-07-15): the earlier amber/primary-looking treatment (amber border + text +
   wash + an "Organiser only" caption underneath) read as too loud. A Locked-visible control should
   now read QUIETLY as "not available in player mode" — a plain, muted, disabled-LOOKING default
   button (grey/secondary), NOT amber and NOT primary. It stays TAPPABLE (opens the organiser-only
   explainer that points to "Enter Admin Mode") — it only LOOKS disabled; we deliberately do NOT set
   the native `disabled` attribute, which would kill the tap-for-explainer. Discoverability of "how
   to use these" is now carried by the always-visible "Enter Admin Mode" toolbar button, so the
   per-control affordance can be quiet. A small, muted lock glyph (see .kiosk-locked-control .lock-icon
   below) signals "locked, unlockable in Admin Mode" rather than looking inertly broken.
   Higher specificity than base .btn / .btn-outline-secondary so it wins without !important. */
.kiosk-locked-control.btn-outline-secondary,
.kiosk-locked-control.btn {
    border-color: #ced4da;
    color: #6c757d;
    background-color: #f8f9fa;
    /* Reads as a resting disabled control, but is still a live tap target for the explainer. */
    cursor: pointer;
    opacity: 1;
}

.kiosk-locked-control.btn-outline-secondary:hover,
.kiosk-locked-control.btn-outline-secondary:focus,
.kiosk-locked-control.btn:hover,
.kiosk-locked-control.btn:focus {
    /* Barely-there hover so it stays quiet but confirms it IS tappable (for the "why"). */
    border-color: #adb5bd;
    color: #6c757d;
    background-color: #eef0f2;
}

/* The lock glyph on a Locked-visible control: SMALL + MUTED (unobtrusive). LockIcon renders a
   16px amber SVG (its intrinsic amber is correct elsewhere — e.g. the game-prep slot locks — so we
   do NOT touch the component); here we scope it down to ~12px and desaturate it to grey so it reads
   as a subtle "locked" hint, not an amber flag. */
.kiosk-locked-control .lock-icon {
    width: 12px;
    height: 12px;
    filter: grayscale(1) opacity(0.65);
    vertical-align: -1px;
}

/* Layout wrapper for a Locked-visible control. The "Organiser only" caption that used to stack
   underneath has been REMOVED (owner feedback 2026-07-15) — the wrapper now just holds the single
   control; kept so the toolbar/panel gap rules below keep working without touching every call site. */
.kiosk-locked-control-wrap {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
}

/* Restore the inter-control gap the wrapper otherwise loses. The normal .end-session-trigger
   carries margin-left: 0.25rem on the button itself, but under kiosk that button is nested inside
   .kiosk-locked-control-wrap, so in the toolbar the gap must sit on the wrapper (the flex item). */
.clubSession-kiosk-actions .kiosk-locked-control-wrap {
    margin-left: 0.25rem;
}

/* Persistent KIOSK badge (● KIOSK) — shown while the kiosk HAT is active (S2a). Copies the v2
   player-board pill shape in a distinct board accent. Solid #0b7285 (NOT a gradient — a gradient's
   lighter stop drops the label below AA); white on #0b7285 = 5.59:1, clears WCAG AA for the small
   label. Distinct class from the Axis-1 body.kiosk-mode fullscreen mechanism so the two axes never
   collide. */
.player-board-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: #0b7285;
    color: #ffffff;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 999px;
    padding: 0.22rem 0.62rem;
    font-size: 0.74rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0.07em;
    flex: 0 0 auto;
    white-space: nowrap;
    box-shadow: 0 1px 6px rgba(13, 16, 20, 0.3);
}

/* S2b-i β + UX rework: the pill becomes the EXIT affordance — a real <button> (mirrors the Axis-1
   super-user "Administer" pill). Reset native button chrome, keep the pill shape, guarantee a ≥40px
   touch target + pointer cursor + a visible focus ring. UX-1/UX-4: it must READ as tappable, so it
   also carries a "tap to exit" sub-label + a chevron — no longer an inert status badge. */
.player-board-pill-btn {
    cursor: pointer;
    min-height: 2.5rem; /* ≥40px courtside/touch target */
    font-family: inherit;
    padding: 0.22rem 0.7rem; /* a touch more room for the sub-label + chevron */
}

.player-board-pill-btn:hover {
    background: #095c6b;
}

.player-board-pill-btn:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: 2px;
}

/* The lock glyph (now the shared <LockIcon>, UX-13) sizing inside the pill. */
.player-board-lock {
    display: inline-flex;
    align-items: center;
    line-height: 1;
}

.player-board-lock svg {
    width: 0.85rem;
    height: 0.85rem;
}

.player-board-pill-label {
    font-weight: 700;
}

/* the affordance sub-label — smaller + lighter than the KIOSK label so the pill reads as a control
   without shouting. UX-7: bumped to 0.72rem for courtside legibility. */
.player-board-pill-sub {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    opacity: 0.9;
    text-transform: lowercase;
    margin-left: 0.1rem;
}

/* #898: the one-tap "Tap for full screen" re-enter affordance, shown on the LOCKED board (kiosk or
   Admin Mode) only while windowed + fullscreen is supported. Courtside-legible + a big touch target,
   but NOT alarming — the calm kiosk teal (matches .player-board-pill), never an amber warning colour.
   Reuses the kiosk accent language; no new brand. */
.clubSession-reenter-fullscreen {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin-top: 0.3rem;
    min-height: 2.75rem; /* ≥44px courtside/touch target */
    padding: 0.4rem 0.95rem;
    background: #0b7285; /* same calm teal as the KIOSK pill */
    color: #ffffff;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 999px;
    font-family: inherit;
    font-size: 0.98rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 1px 6px rgba(13, 16, 20, 0.3);
}

.clubSession-reenter-fullscreen:hover {
    background: #095c6b;
}

.clubSession-reenter-fullscreen:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: 2px;
}

.clubSession-reenter-fullscreen-glyph {
    font-size: 1.1rem;
    line-height: 1;
}

/* S2b-i rework: the ADMIN MODE pill — a DISTINCT (amber/elevated) colour from the calm teal KIOSK pill,
   so "you are elevated, changes are live" reads at a glance. Mirrors the Axis-1 super-user write-pill's
   elevated-amber language. Not a button (the banner carries the controls). */
.player-board-pill-admin {
    background: #9a6a00;
    border-color: rgba(255, 255, 255, 0.5);
}

/* The Admin-Mode banner under the session title: the live-changes line + Exit Admin Mode (primary), with
   "Stop kiosk mode" demoted to its own row (MUST-3). Amber-tinted so it is clearly a different state from
   Locked kiosk. */
.clubSession-admin-mode-banner {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.25rem;
    padding: 0.35rem 0.6rem;
    border: 1px solid #9a6a00;
    border-radius: 0.4rem;
    background: #fdf6e3;
}

/* Row 1: the live-changes text + the OBVIOUS first target (Exit Admin Mode). */
.clubSession-admin-mode-primary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

/* Row 2 (MUST-3): "Stop kiosk mode" separated by a top divider + demoted to a link, so a hurried tap
   can't end the shared-device session by mistake. */
.clubSession-admin-mode-stop-row {
    border-top: 1px dashed #cbb27a;
    padding-top: 0.3rem;
}

.admin-mode-stop-kiosk-btn {
    color: #8a5a00;
    font-size: 0.78rem;
    text-decoration: underline;
    padding: 0 0.1rem;
}

.clubSession-admin-mode-text {
    font-size: 0.82rem;
    color: #6b4a00;
    font-weight: 600;
}

/* MUST-4: at <=15s idle-remaining, the banner shifts to a prominent amber warning. */
.clubSession-admin-mode-banner.is-warning {
    background: #fff3cd;
    border-color: #c9860a;
    box-shadow: 0 0 0 2px rgba(201, 134, 10, 0.25);
}

.clubSession-admin-mode-banner.is-warning .clubSession-admin-mode-text {
    color: #7a4f00;
    font-weight: 700;
}

/* MUST-5: the brief "Returned to player mode" flash on the Admin Mode -> Locked transition. */
.clubSession-returned-flash {
    display: inline-block;
    margin-top: 0.2rem;
    padding: 0.2rem 0.55rem;
    border-radius: 0.35rem;
    background: #0b7285;
    color: #ffffff;
    font-size: 0.8rem;
    font-weight: 600;
    animation: returned-flash-fade 3s ease-in forwards;
}

@keyframes returned-flash-fade {
    0% { opacity: 0; }
    10% { opacity: 1; }
    80% { opacity: 1; }
    100% { opacity: 0; }
}

.player-board-dot {
    width: 0.46rem;
    height: 0.46rem;
    border-radius: 50%;
    background: #ffffff;
    animation: player-board-dot-pulse 1.3s ease-in-out infinite;
}

@keyframes player-board-dot-pulse {
    0% { transform: scale(1); opacity: 1; }
    50% { transform: scale(1.25); opacity: 0.72; }
    100% { transform: scale(1); opacity: 1; }
}
