/* ===== OpenDyslexic (accessibility unit, SIL OFL 1.1, self-hosted) ===== */
/* Binaries live in assets/fonts/ (committed alongside OFL.txt). Applied by
   swapping the single --font-family token when data-font="dyslexic". */
@font-face {
    font-family: 'OpenDyslexic';
    src: url('assets/fonts/OpenDyslexic-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: 'OpenDyslexic';
    src: url('assets/fonts/OpenDyslexic-Bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* ===== CSS Variables ===== */
:root {
    /* ===== Indigo Studio palette (canonical) =====
       These values are the canonical Indigo Studio palette, shared BY HAND with
       gadget-match-server console.css. There is no shared token file (deliberate,
       spec decision B) — if you change a brand/surface value here, change it in
       console.css too. Token NAMES are kept from the v4.x game so component rules
       are untouched; only VALUES changed in the reskin. */
    --color-primary: #3d337c;        /* brand indigo */
    --color-primary-dark: #332a68;   /* hover/pressed */
    --color-primary-fill: #3d337c;   /* brand fill behind white text (dark-safe pair) */
    --color-primary-fill-hover: #332a68; /* hover state of the fill above */
    --color-primary-light: #efeaf7;  /* tint: selected bg, hero-ground */
    --color-secondary: #73c3eb;      /* sky accent */
    --color-secondary-dark: #4aa8d8; /* sky hover */
    --color-secondary-light: #e3f4fb;/* sky tint */
    --color-success: #2f8f72;
    /* WCAG AA (Review Client I5). Previous values and their measured ratios
       against white: --color-warning #e0a53b 2.19:1, --color-info #73c3eb
       1.95:1, chip--social #cd8a3b 2.88:1, chip--sustainability #59923f
       3.74:1, chip--independence #2f8f72 3.97:1, chip--health #c15a6a
       4.25:1. Four of six category chips failed — and they are the primary
       semantic label on every technology and scenario card, at 12px.
       chip--mobility (#3f77b5, 4.65:1) and chip--memory (#8a63ad, 4.70:1)
       already passed and are unchanged. Re-measure before any further tweak. */
    --color-warning: #8a5a00;          /* was #e0a53b */
    --color-warning-light: #fdf4e3;
    --color-error: #c0392b;
    --color-info: #10618c;             /* was #73c3eb */
    --color-background: #eef0f4;      /* canvas */
    --color-surface: #ffffff;         /* panels/cards */
    --color-text: #211f2e;            /* ink */
    --color-text-secondary: #615e6d;  /* was #7c7989 (4.24:1) */
    --color-border: #e2e3ea;          /* hairline */
    --color-judge: #8a63ad;           /* judge accent (memory-purple) */
    --color-winner: #e0a53b;          /* was #FFD700 gold */
    /* Six technology/scenario category colours (identical to the splash chips,
       console.css and landing.css — keep all four files in sync). */
    --cat-health: #96384a;             /* was #c15a6a */
    --cat-independence: #1f6b54;       /* was #2f8f72 */
    --cat-mobility: #3f77b5;
    --cat-memory: #8a63ad;
    --cat-social: #8a5312;             /* was #cd8a3b */
    --cat-sustainability: #3d6a28;     /* was #59923f */
    /* Amber winner accent */
    --color-winner-surface: #fffdf6;
    --color-winner-ink: #3a2a00;

    /* Typography */
    /* Single font token: a future font swap (accessibility unit) is a one-token change. */
    --font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-family-fallback: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-scale: 1; /* accessibility text-size hook: multiply the type scale */
    --font-size-base: calc(16px * var(--font-scale));
    --font-size-small: calc(14px * var(--font-scale));
    --font-size-large: calc(20px * var(--font-scale));
    --font-size-xlarge: calc(28px * var(--font-scale));
    --font-weight-normal: 400;
    --font-weight-medium: 500;
    --font-weight-bold: 700;
    
    /* Spacing */
    --spacing-xs: 4px;
    --spacing-sm: 8px;
    --spacing-md: 16px;
    --spacing-lg: 24px;
    --spacing-xl: 32px;
    
    /* Shadows */
    --shadow-sm: 0 1px 3px rgba(30,28,60,.07);
    --shadow-md: 0 6px 18px rgba(40,34,86,.10);
    --shadow-lg: 0 14px 40px rgba(40,34,86,.14);
    
    /* Border Radius */
    --radius-sm: 8px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --radius-xl: 16px;
    --radius-pill: 999px;
}

/* ===== Named themes =====
   Dark ships in this reskin. The structure is deliberately named-theme (not a
   light/dark boolean) so the companion Accessibility unit can add
   :root[data-theme="high-contrast"] as one more block with no rework.
   Triggered by BOTH the OS preference AND an explicit data-theme attribute;
   an explicit attribute always wins (it comes later / is more specific). */

/* The dark token values, shared by the media query and the [data-theme] hook. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --color-primary: #9a8ee0;
        --color-primary-dark: #b4abe9;
        --color-secondary: #8ed0f0;
        --color-background: #141220;
        --color-surface: #1f1c2c;
        --color-primary-light: #1b1830;
        --color-text: #efedf7;
        --color-text-secondary: #928ea3;
        --color-border: #332f43;
        --color-secondary-light: #22304a;   /* deep sky tint: near-white text legible in dark */
        --color-winner-surface: #2b2410;    /* deep amber tint: near-white text legible in dark */
        --color-primary-fill: #4a3f86;        /* deep indigo: white text stays >= 4.5:1 in dark */
        --color-primary-fill-hover: #5b4fa0;
        --shadow-sm: 0 1px 3px rgba(0,0,0,.45);
        --shadow-md: 0 8px 22px rgba(0,0,0,.5);
        --shadow-lg: 0 16px 44px rgba(0,0,0,.55);
    }
    /* WCAG 1.4.11 non-text contrast (Review Client I5 fix round 1): the
       darkened --cat-* chip fills measure as low as 2.35:1 against the dark
       --color-surface (#1f1c2c) -- below the 3:1 floor for a UI component's
       boundary. A token override would not help: in-game chips get their
       fill INLINE from the broadcast card-set taxonomy (ui.js sets
       chip.style.backgroundColor directly), so an imported deck's colours
       would still be unringed. A theme-scoped ring works for every set.
       rgba(255,255,255,.45) over #1f1c2c blends to ~#84828b, 4.40:1 against
       the surface -- comfortably clear of 3:1. */
    /* Final-review wave M7: exclude high-contrast explicitly. This media
       query fires on an OS-dark phone whatever the app theme is, and
       :not([data-theme="light"]) therefore also matches
       [data-theme="high-contrast"] -- whose own .chip rule sets
       background/color/border !important but says nothing about box-shadow,
       so the ring landed on top of an already white-bordered chip. */
    :root:not([data-theme="light"]):not([data-theme="high-contrast"]) .chip {
        box-shadow: 0 0 0 1px rgba(255,255,255,.45);
    }

    /* Final-review wave I2: --color-warning/--color-error are painted as
       TEXT by the phase countdown. Both were darkened (or were always dark)
       for AA on the light surface, and neither token is overridden for dark,
       so the last stretch of every round read at 2.81:1 (warning) / 3.06:1
       (urgent) on --color-surface #1f1c2c. The tokens must NOT be
       re-lightened -- they are banner BACKGROUNDS elsewhere, where white ink
       depends on them staying dark -- so the countdown rule takes its own
       dark-theme colour. High-contrast keeps its own amber/red accents. */
    :root:not([data-theme="light"]):not([data-theme="high-contrast"]) .phase-timer__countdown--warning {
        color: #e0a53b;   /* 7.63:1 on --color-surface, 8.46:1 on --color-background */
    }
    :root:not([data-theme="light"]):not([data-theme="high-contrast"]) .phase-timer__countdown--urgent {
        color: #ff8a7a;   /* 7.27:1 on --color-surface, 8.06:1 on --color-background */
    }
}

/* Explicit override wins over the OS preference in both directions. */
:root[data-theme="dark"] {
    --color-primary: #9a8ee0;
    --color-primary-dark: #b4abe9;
    --color-secondary: #8ed0f0;
    --color-background: #141220;
    --color-surface: #1f1c2c;
    --color-primary-light: #1b1830;
    --color-text: #efedf7;
    --color-text-secondary: #928ea3;
    --color-border: #332f43;
    --color-secondary-light: #22304a;   /* deep sky tint: near-white text legible in dark */
    --color-winner-surface: #2b2410;    /* deep amber tint: near-white text legible in dark */
    --color-primary-fill: #4a3f86;        /* deep indigo: white text stays >= 4.5:1 in dark */
    --color-primary-fill-hover: #5b4fa0;
    --shadow-sm: 0 1px 3px rgba(0,0,0,.45);
    --shadow-md: 0 8px 22px rgba(0,0,0,.5);
    --shadow-lg: 0 16px 44px rgba(0,0,0,.55);
}
/* Same non-text-contrast ring as the media-query block above (Review Client I5 fix round 1). */
:root[data-theme="dark"] .chip {
    box-shadow: 0 0 0 1px rgba(255,255,255,.45);
}

/* Same countdown ink as the media-query block above (final-review wave I2):
   an explicit dark choice must not fall back to the light-surface tokens. */
:root[data-theme="dark"] .phase-timer__countdown--warning {
    color: #e0a53b;   /* 7.63:1 on --color-surface, 8.46:1 on --color-background */
}
:root[data-theme="dark"] .phase-timer__countdown--urgent {
    color: #ff8a7a;   /* 7.27:1 on --color-surface, 8.06:1 on --color-background */
}

/* :root[data-theme="light"] needs no overrides — it simply keeps the :root
   light defaults and, via :not([data-theme="light"]) above, blocks the dark
   media query. Present as an explicit anchor for the companion unit. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ===== Theme: High-contrast (accessibility unit) ===== */
/* Overrides light/dark entirely. Near-black surfaces, white text + white
   hairlines (thicker), one bright amber accent, outlined category chips. */
:root[data-theme="high-contrast"] {
    --color-primary: #ffdd00;
    --color-primary-dark: #ffe94d;
    --color-primary-light: #1a1a1a;
    --color-secondary: #ffdd00;
    --color-secondary-dark: #ffe94d;
    --color-secondary-light: #1a1a1a;
    --color-background: #000000;
    --color-surface: #0a0a0a;
    --color-text: #ffffff;
    --color-text-secondary: #ffffff;
    --color-border: #ffffff;
    --color-judge: #ffdd00;
    --color-success: #ffdd00;
    --color-warning: #ffdd00;
    --color-warning-light: #1a1a1a;
    --color-error: #ff6b6b;
    --color-info: #ffdd00;

    /* Scenario heading band: near-black (not set = leaks the light/dark
       indigo value), paired with its existing white text. */
    --color-primary-fill: #0a0a0a;
    --color-primary-fill-hover: #1a1a1a;

    /* Winner celebration maps onto the single amber accent. The ribbon text
       (--color-winner-ink) must stay black — it sits on the amber ribbon
       (--color-winner), so amber ink would be invisible (amber-on-amber). */
    --color-winner: #ffdd00;
    --color-winner-surface: #0a0a0a;
    --color-winner-ink: #000000;

    --shadow-sm: 0 0 0 1px #ffffff;
    --shadow-md: 0 0 0 2px #ffffff;
    --shadow-lg: 0 0 0 2px #ffffff;
}

/* Thicker hairlines throughout high-contrast. */
:root[data-theme="high-contrast"] .container,
:root[data-theme="high-contrast"] .card,
:root[data-theme="high-contrast"] .a11y-panel,
:root[data-theme="high-contrast"] .a11y-option span {
    border-width: 2px;
}

/* Category chips become OUTLINED labels (colour alone is insufficient at this
   contrast): transparent fill, white border + white text, name still reads.
   `.chip` is the reskin's category-chip class (emoji->chip swap, reskin §6). */
:root[data-theme="high-contrast"] .chip {
    background: transparent !important;
    color: #ffffff !important;
    border: 2px solid #ffffff !important;
}

/* Primary action + focus use the amber accent with black text for legibility. */
:root[data-theme="high-contrast"] .btn--primary {
    background: #ffdd00;
    color: #000000;
    border-color: #ffdd00;
}
:root[data-theme="high-contrast"] .a11y-option input:checked + span {
    background: #ffdd00;
    color: #000000;
    border-color: #ffdd00;
}
:root[data-theme="high-contrast"] :focus-visible {
    outline: 3px solid #ffdd00;
    outline-offset: 2px;
}

/* Final-review fix wave: components whose base rule hardcodes white text on
   a background that resolves to amber (#ffdd00) under high-contrast — force
   black ink so they read black-on-amber, matching .btn--primary above.
   (.reconnect-info is NOT included: it sits on --color-warning-light, which
   stays near-black in HC — forcing black text there would be illegible.) */
:root[data-theme="high-contrast"] .card--submitted::after { color: #000000; }
:root[data-theme="high-contrast"] .error-banner--warning,
:root[data-theme="high-contrast"] .error-banner--info { color: #000000; }
:root[data-theme="high-contrast"] .btn--success,
:root[data-theme="high-contrast"] .btn--warning { color: #000000; }
:root[data-theme="high-contrast"] .btn--outline:hover { color: #000000; }

/* Black on #ff6b6b measures 7.6:1; white measures 2.77:1. The previous
   comment ("forcing black text there would be illegible") had it backwards —
   and this is the theme a low-vision user chose BECAUSE of their vision.
   (Review Client I5.) */
:root[data-theme="high-contrast"] .error-banner--error {
    color: #000;
    border: 2px solid #000;
}

/* ===== Font: Dyslexia-friendly (accessibility unit) ===== */
/* One-token swap: takes effect everywhere --font-family is used. */
:root[data-font="dyslexic"] {
    --font-family: 'OpenDyslexic', var(--font-family-fallback, sans-serif);
}

/* ===== Base Styles ===== */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    line-height: 1.6;
    color: var(--color-text);
    background: var(--color-background);
    min-height: 100vh;
}

#app {
    padding: var(--spacing-lg);
    padding-top: var(--spacing-sm);
    padding-bottom: var(--spacing-lg); /* Reduced - container has its own margin */
}


/* ===== Container ===== */
.container {
    max-width: 1200px;
    margin: 0 auto;
    margin-bottom: 80px; /* Space before footer - prevents overlap */
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    padding: var(--spacing-xl);
    box-shadow: var(--shadow-lg);
}

/* ===== Typography ===== */
.title {
    font-size: var(--font-size-xlarge);
    font-weight: var(--font-weight-bold);
    color: var(--color-primary);
    text-align: center;
    margin-bottom: var(--spacing-md);
}

.subtitle {
    font-size: var(--font-size-base);
    color: var(--color-text-secondary);
    text-align: center;
    margin-bottom: var(--spacing-xl);
}

/* ===== Logo ===== */
.logo {
    display: block;
    max-width: 100%;
    height: auto;
}

.logo--large {
    width: 320px;
    max-width: 90%;
    margin: 0 auto var(--spacing-xl);
}

.logo--small {
    width: 100px;
    height: auto;
}

.game-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--spacing-lg);
    gap: var(--spacing-md);
}

.game-header__info {
    flex: 1;
}

/* ===== Buttons ===== */
.btn {
    padding: 12px 24px;
    border: none;
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    transition: all 0.2s ease;
    font-family: inherit;
}

.btn:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

.btn:active {
    transform: translateY(0);
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

.btn--primary {
    background: var(--color-primary-fill);
    color: white;
}

.btn--primary:hover:not(:disabled) {
    background: var(--color-primary-fill-hover);
}

/* Card-selection prompt: shown only while no card is chosen. This used to be
   the submit/select-winner <button> itself, disabled and restyled as text —
   a screen reader announced it as "Select a card to submit, dimmed button".
   It is now plain paragraph text; the real button stays a permanently
   display:none programmatic hook only (see index.html). (Review Client M10.) */
.hand-instruction {
    color: var(--color-text-secondary);
    font-weight: var(--font-weight-medium);
    text-align: center;
    padding: var(--spacing-xs) 0;
    margin: 0;
}

.btn--secondary {
    background: transparent;
    color: var(--color-primary);
    border: 1px solid var(--color-border);
}

.btn--secondary:hover:not(:disabled) {
    background: var(--color-primary-light);
    border-color: var(--color-primary);
}

.btn--small {
    padding: 6px 12px;
    font-size: var(--font-size-small);
}

.btn--warning {
    background: var(--color-warning);
    color: #ffffff;
}

.btn--warning:hover:not(:disabled) {
    /* Final-review wave M3: #c88a29 was LIGHTER than the AA base
       (--color-warning #8a5a00) and put white ink at 2.95:1. A hover state
       should darken: #6e4800 measures 8.11:1 with white. */
    background: #6e4800;
}

.btn--outline {
    background: transparent;
    color: var(--color-primary);
    border: 1px solid var(--color-primary);
}

.btn--outline:hover:not(:disabled) {
    background: var(--color-primary);
    color: #ffffff;
}

.btn--small {
    padding: 6px 12px;
    font-size: var(--font-size-small);
}

/* Link-styled button: text-only, used for secondary CTAs like "Facilitate a
 * game" on the home screen where it shouldn't compete with the primary join
 * controls. */
.btn--link {
    background: transparent;
    color: var(--color-primary);
    border: none;
    padding: 6px 4px;
    font-weight: 600;
    text-decoration: underline;
}

.btn--link:hover:not(:disabled) {
    background: transparent;
    color: var(--color-primary-dark);
}

.facilitate-cta {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--color-primary-light);
    text-align: center;
}

.facilitate-cta small {
    display: block;
    color: var(--color-text-secondary, #666);
    font-size: var(--font-size-small);
    margin-top: 2px;
}

/* Facilitated end-screen: "Go home" escape link under the Play Again button. */
.end-screen__leave-link {
    display: inline-block;
    margin-top: var(--spacing-md);
    color: var(--color-text-secondary, #666);
    text-decoration: underline;
    font-size: var(--font-size-small);
}

.end-screen__leave-link:hover {
    color: var(--color-primary);
}

/* Facilitator end-screen: "N of M players waiting" indicator above the
   Create New Game button. */
.play-again-count {
    margin-top: var(--spacing-md);
    color: var(--color-text-secondary, #666);
    font-style: italic;
}

.btn--success {
    background: var(--color-success);
    color: white;
}

.button-group {
    display: flex;
    gap: var(--spacing-md);
    margin-top: var(--spacing-lg);
}

/* When a rejoinable session is found, the rejoin panel IS the screen: hide
   the join form, Join button, auth CTA and workshop banner so "Rejoin Game"
   sits right under the logo instead of below the fold (tester report). */
#join-screen.join-screen--rejoin .form-group,
#join-screen.join-screen--rejoin .button-group,
#join-screen.join-screen--rejoin .auth-cta,
#join-screen.join-screen--rejoin .workshop-banner {
    display: none;
}

#join-screen.join-screen--rejoin .reconnect-info {
    margin-top: var(--spacing-lg);
}

/* ===== Reconnect Info ===== */
.reconnect-info {
    margin-top: var(--spacing-xl);
    padding: var(--spacing-lg);
    background: var(--color-warning-light);
    border: 2px solid var(--color-warning);
    border-radius: var(--radius-md);
    text-align: center;
}

.reconnect-info h3 {
    margin-bottom: var(--spacing-md);
    color: var(--color-text);
    font-size: var(--font-size-large);
}

.session-details {
    margin: var(--spacing-md) 0;
    padding: var(--spacing-md);
    background: var(--color-surface);
    border-radius: var(--radius-sm);
}

.session-details p {
    margin: var(--spacing-sm) 0;
    color: var(--color-text);
    font-size: var(--font-size-base);
}

.session-details strong {
    color: var(--color-text-secondary);
    font-weight: var(--font-weight-medium);
}

.session-details span {
    color: var(--color-primary);
    font-weight: var(--font-weight-bold);
}

.reconnect-actions {
    display: flex;
    gap: var(--spacing-md);
    justify-content: center;
    margin-top: var(--spacing-md);
}

.reconnect-actions .btn {
    flex: 1;
    max-width: 200px;
}

/* ===== Form Elements ===== */
.form-group {
    margin-bottom: var(--spacing-lg);
}

.form-group label {
    display: block;
    margin-bottom: var(--spacing-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
}

.form-group input {
    width: 100%;
    padding: 12px;
    border: 2px solid var(--color-border);
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    font-family: inherit;
    transition: border-color 0.2s;
}

.form-group input:focus {
    outline: none;
    border-color: var(--color-primary);
}

.form-group input:read-only {
    background: var(--color-background);
    color: var(--color-text-secondary);
    cursor: not-allowed;
}

.form-group small {
    display: block;
    margin-top: var(--spacing-xs);
    color: var(--color-text-secondary);
    font-size: var(--font-size-small);
}

/* ===== Game Code Display ===== */
.game-code-display {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-sm);
    padding: var(--spacing-sm) var(--spacing-md);
    background: var(--color-primary-light);
    color: var(--color-primary);
    border-radius: var(--radius-pill);
    margin-bottom: var(--spacing-xl);
}

.game-code-display strong {
    font-size: var(--font-size-large);
    font-family: 'Courier New', monospace;
    letter-spacing: 2px;
}

/* ===== Player List ===== */
.player-list {
    list-style: none;
    margin: var(--spacing-lg) 0;
    max-width: 400px;
    margin-left: auto;
    margin-right: auto;
}

.player-list li {
    padding: var(--spacing-md);
    margin-bottom: var(--spacing-sm);
    background: var(--color-surface);
    border: 2px solid var(--color-border);
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-md);
}

.player-list__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-md);
}

.player-list__info {
    flex: 1;
}

.skip-btn {
    flex-shrink: 0;
}

/* Connection Indicator */
.connection-indicator {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-block;
    box-shadow: 0 0 4px rgba(0,0,0,0.2);
}

.connection-indicator--online {
    background: var(--color-success);
    box-shadow: 0 0 6px rgba(47, 143, 114, 0.6);
}

.connection-indicator--offline {
    background: var(--color-error);
    box-shadow: 0 0 6px rgba(192, 57, 43, 0.6);
}

.player-list li.player--you {
    background: var(--color-primary-light);
    border-color: var(--color-primary);
}

.player-list li.player--disconnected {
    opacity: 0.5;
}

.player-count {
    text-align: center;
    font-size: var(--font-size-large);
    margin: var(--spacing-lg) 0;
    font-weight: var(--font-weight-bold);
}

.lobby-status {
    text-align: center;
    margin: var(--spacing-lg) 0;
    color: var(--color-text-secondary);
}

/* ===== Round Header ===== */
.round-header {
    margin-bottom: var(--spacing-lg);
}

.round-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--spacing-md);
    background: var(--color-background);
    border-radius: var(--radius-md);
}

/* ===== Scenario Card ===== */
.scenario-section {
    margin-bottom: var(--spacing-xl);
}

.scenario-card {
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-md);
    overflow: hidden;
    padding: 0; /* band + body supply their own padding */
}

.scenario-card--small {
    padding: var(--spacing-lg);
    margin-bottom: var(--spacing-lg);
}

.scenario-heading,
#reveal-scenario-heading,
#fac-scenario-heading {
    background: var(--color-primary-fill);
    color: #ffffff;
    font-size: var(--font-size-large);
    font-weight: var(--font-weight-bold);
    text-align: left;
    margin: 0;
    padding: var(--spacing-lg) var(--spacing-xl) var(--spacing-md);
}

.scenario-heading::before,
#reveal-scenario-heading::before,
#fac-scenario-heading::before {
    content: "Challenge";
    display: block;
    font-size: var(--font-size-small);
    font-weight: var(--font-weight-medium);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.85;
    margin-bottom: var(--spacing-xs);
}

.scenario-icons {
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: var(--spacing-sm);
    padding: var(--spacing-md) var(--spacing-xl) 0;
    margin: 0;
}

.scenario-text,
#reveal-scenario-text,
#fac-scenario-text {
    font-size: 18px;
    line-height: 1.6;
    text-align: left;
    color: var(--color-text);
    background: var(--color-background);
    margin: var(--spacing-md) 0 0;
    padding: var(--spacing-md) var(--spacing-xl) var(--spacing-lg);
}

/* ===== Technology Cards ===== */
.card-hand {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    gap: var(--spacing-md);
    flex-wrap: wrap;
    margin: var(--spacing-lg) 0;
    min-height: 200px;
}

.card {
    width: 230px;
    height: auto;
    min-height: 322px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    padding: var(--spacing-md);
    box-shadow: var(--shadow-md);
    transition: all 0.2s ease;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-sm);
    overflow: hidden;
}

.card--selectable {
    cursor: pointer;
}

.card--selectable:hover {
    transform: translateY(-8px);
    box-shadow: var(--shadow-lg);
    border-color: var(--color-primary);
}

/* Keyboard focus ring for the now-focusable cards (tabindex=0, task 5a).
   :focus-visible only, so mouse clicks don't get a ring. --color-primary
   flips per theme (#3d337c light / #9a8ee0 dark / #ffdd00 high-contrast),
   so the ring clears 3:1 against the surrounding background in all three. */
.card--selectable:focus-visible {
    outline: 3px solid var(--color-primary);
    outline-offset: 2px;
}

.card--selected {
    border: 3px solid var(--color-secondary);
    background: var(--color-secondary-light);
    transform: translateY(-8px) scale(1.03);
    box-shadow: 0 0 0 3px rgba(115, 195, 235, 0.35), var(--shadow-lg);
}

.card--winner {
    border: 3px solid var(--color-winner);
    background: var(--color-winner-surface);
    box-shadow: 0 0 0 4px rgba(224, 165, 59, 0.25), var(--shadow-lg);
    position: relative;
}

/* Winner card wrapper on the reveal screen. The .card--winner element is
   appended inside; the .card__winner-ribbon lozenge is appended as a sibling
   and positioned above the card. The card itself is overflow:hidden, so a
   lozenge inside it would be clipped and would sit over the card title — the
   slot lets it float clear above the card. */
.winner-card-slot {
    position: relative;
    display: inline-block;
    margin-top: 22px;
}

.card__winner-ribbon {
    position: absolute;
    top: -13px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--color-winner);
    color: var(--color-winner-ink);
    font-size: 13px;
    font-weight: var(--font-weight-bold);
    letter-spacing: 0.04em;
    padding: 3px 14px;
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-sm);
    z-index: 2;
    white-space: nowrap;
    pointer-events: none;
}

.card--mine {
    border: 2px solid var(--color-secondary);
    background: var(--color-secondary-light);
    box-shadow: 0 0 0 3px rgba(115, 195, 235, 0.2);
}
.card--mine.card--selected {
    border: 3px solid var(--color-secondary);
    background: var(--color-secondary-light);
}

.card__name {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-bold);
    text-align: center;
    color: var(--color-text);
    height: auto;
    min-height: 52px;
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card__image {
    width: 100%;
    height: 190px;
    overflow: hidden;
    border-radius: var(--radius-md);
    background: var(--color-background);
    display: flex;
    align-items: center;
    justify-content: center;
}

.card__image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.card__player-name {
    font-size: var(--font-size-small);
    font-weight: var(--font-weight-medium);
    text-align: center;
    color: var(--color-text-secondary);
    padding: var(--spacing-xs) var(--spacing-sm);
    background: var(--color-background);
    border-radius: var(--radius-sm);
    margin-top: auto;
    border-top: 1px solid var(--color-border);
}

.card__icons {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--spacing-xs);
    flex-wrap: wrap;
    min-height: 24px;
}

/* ===== Category chips (replace emoji icons) ===== */
.chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: var(--font-weight-medium);
    line-height: 1.5;
    color: #ffffff;
    background: var(--color-text-secondary); /* fallback if no category class */
    white-space: nowrap;
}
.chip--health        { background: var(--cat-health); }
.chip--independence  { background: var(--cat-independence); }
.chip--mobility      { background: var(--cat-mobility); }
.chip--memory        { background: var(--cat-memory); }
.chip--social        { background: var(--cat-social); }
.chip--sustainability{ background: var(--cat-sustainability); }

/* ===== Submission Status ===== */
.submission-status {
    text-align: center;
    padding: var(--spacing-lg);
    background: var(--color-background);
    border-radius: var(--radius-md);
    margin: var(--spacing-lg) 0;
}

.submission-progress {
    margin-top: var(--spacing-md);
    font-weight: var(--font-weight-medium);
}

.submitted-card-display {
    display: flex;
    justify-content: center;
    margin-bottom: var(--spacing-md);
}

.submitted-card-display:empty {
    display: none;
}

/* ===== Submission Feedback Row (per-player card backs during SUBMIT) ===== */
.submission-feedback {
    margin-top: var(--spacing-lg);
    margin-bottom: var(--spacing-lg);
}

.submission-feedback__heading {
    font-size: var(--font-size-small);
    color: var(--color-text-secondary);
    margin-bottom: var(--spacing-sm);
    text-align: center;
}

.submission-feedback__row {
    display: flex;
    justify-content: center;
    gap: var(--spacing-md);
    flex-wrap: wrap;
}

.card-back {
    width: 120px;
    height: 168px;
    border-radius: var(--radius-lg);
    background: var(--color-primary-light);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-md);
    transition: border-color 0.3s ease, background 0.3s ease;
    box-sizing: border-box;
}

.card-back--waiting {
    border: 3px dashed var(--color-text-secondary);
    background: var(--color-background);
}

.card-back--submitted {
    border: 3px solid var(--color-primary);
    background: var(--color-primary-light);
}

.card-back__logo {
    width: 80%;
    max-height: 70%;
    object-fit: contain;
    opacity: 0.85;
}

.card-back__name {
    font-size: var(--font-size-small);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
    margin-top: var(--spacing-xs);
    text-align: center;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.card-back--disconnected {
    opacity: 0.4;
}

/* ===== Judge Waiting ===== */
.judge-message {
    text-align: center;
    padding: var(--spacing-xl);
}

.judge-message h3 {
    margin-bottom: var(--spacing-md);
}

/* ===== Reveal Screen ===== */
.winner-display {
    text-align: center;
    margin: var(--spacing-xl) 0;
}


.winner-name {
    margin-top: var(--spacing-md);
    font-size: var(--font-size-large);
}

#winning-card-display {
    margin: var(--spacing-lg) auto;
}

/* ===== Scoreboard ===== */
.scoreboard-section {
    margin: var(--spacing-xl) 0;
}

.scoreboard-section h3 {
    text-align: center;
    margin-bottom: var(--spacing-md);
}

.scoreboard {
    list-style: none;
    max-width: 500px;
    margin: 0 auto;
}

.scoreboard li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--spacing-md);
    margin-bottom: var(--spacing-sm);
    background: var(--color-background);
    border-radius: var(--radius-md);
}

.scoreboard__rank {
    font-weight: var(--font-weight-bold);
    margin-right: var(--spacing-md);
}

.scoreboard__name {
    flex: 1;
}

.scoreboard__score {
    font-size: var(--font-size-large);
}

.score--updated {
    animation: score-pulse 0.5s;
    color: var(--color-success);
}

/* ===== Game Over ===== */
.game-over-content {
    text-align: center;
    margin: var(--spacing-xl) 0;
}

.thanks-message {
    text-align: center;
    margin: var(--spacing-lg) 0;
    color: var(--color-primary);
}

.thanks-message h2 {
    font-size: var(--font-size-xlarge);
    font-weight: var(--font-weight-bold);
}

.celebration {
    font-size: 48px;
    margin: var(--spacing-md) 0;
}

/* ===== Modal ===== */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1000;
    display: none;
    align-items: center;
    justify-content: center;
}

.modal--visible {
    display: flex;
}

.modal__overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
}

.modal__content {
    position: relative;
    background: var(--color-surface);
    color: var(--color-text);
    border-radius: var(--radius-xl);
    padding: var(--spacing-xl);
    max-width: 500px;
    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3);
    animation: modalSlideIn 0.3s ease-out;
}

.modal__title {
    font-size: var(--font-size-large);
    font-weight: var(--font-weight-bold);
    margin-bottom: var(--spacing-lg);
    text-align: center;
}

.modal__card-preview {
    display: flex;
    justify-content: center;
    margin-bottom: var(--spacing-lg);
}

.modal__message {
    text-align: center;
    color: var(--color-text-secondary);
    margin-bottom: var(--spacing-lg);
}

.modal__actions {
    display: flex;
    gap: var(--spacing-md);
    justify-content: center;
}

/* Mobile: let the confirm-selection popup's preview card use the available
   width instead of the cramped fixed base-card size, so the photo + name stay
   legible. Scoped to #confirmation-modal (the play/judge confirm popup); other
   modals and desktop are untouched. */
@media (max-width: 768px) {
    /* Cap the popup to the viewport and lay it out as a flex column so the
       Cancel/Confirm buttons are always on screen; the card preview is the
       shrinkable/scrollable middle. */
    #confirmation-modal .modal__content {
        width: min(88vw, 400px);
        max-height: 92vh;  /* fallback: browsers without dvh (iOS 15.0–15.3, Chrome <108) */
        max-height: 92dvh;
        display: flex;
        flex-direction: column;
    }
    #confirmation-modal .modal__actions {
        flex: 0 0 auto;
    }
    /* On a phone the card preview + Confirm/Cancel are self-explanatory — drop
       the visible title and instruction line (they left a big gap between
       them). The title is visually hidden rather than display:none: it is
       the dialog's aria-labelledby target, and a display:none element is
       excluded from accessible-name computation, which would leave the
       dialog with no name at all on mobile (Review Client I2). */
    #confirmation-modal .modal__title {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
        border: 0;
    }
    #confirmation-modal .modal__message {
        display: none;
    }
    #confirmation-modal .modal__card-preview {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
    }
    #confirmation-modal .modal__card-preview .card {
        width: 100%;
        max-width: 300px;
        height: auto;
    }
    #confirmation-modal .modal__card-preview .card__image {
        height: auto;
        aspect-ratio: 4 / 3;
    }
}

/* ===== Loading ===== */
#loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.5);
    display: none;
    /* Stack spinner / message / Cancel button vertically, not in a row — a
       long showLoading() message must not push the Cancel button toward or
       off the edge of a narrow phone. (Review Client C1 / Task 3.1.) */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-md);
    z-index: 999;
}

#loading-overlay.visible {
    display: flex;
}

/* A blocking overlay for an operation that can take minutes must be
   escapable. (Review Client C1.) */
.loading-overlay__cancel {
    margin-top: var(--spacing-lg);
    min-height: 44px;   /* WCAG 2.5.5 target size */
}

.loading-spinner {
    width: 50px;
    height: 50px;
    border: 5px solid var(--color-border);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

.loading-text {
    color: white;
    margin-top: var(--spacing-md);
    font-size: var(--font-size-large);
}

/* ===== Error Banner ===== */
#error-banner-container {
    position: fixed;
    top: var(--spacing-lg);
    left: 50%;
    transform: translateX(-50%);
    z-index: 1001;
    max-width: 500px;
    width: 90%;
}

.error-banner {
    padding: var(--spacing-md);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    display: flex;
    justify-content: space-between;
    align-items: center;
    animation: slideDown 0.3s ease-out;
}

.error-banner--error {
    background: var(--color-error);
    color: white;
}

.error-banner--warning {
    background: var(--color-warning);
    color: white;
}

.error-banner--info {
    background: var(--color-info);
    color: white;
}

.error-banner__close {
    background: none;
    border: none;
    color: white;
    font-size: 24px;
    cursor: pointer;
    padding: 0;
    margin-left: var(--spacing-md);
}

/* ===== Animations ===== */
@keyframes spin {
    to { transform: rotate(360deg); }
}

@keyframes pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.1); }
}

@keyframes score-pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.2); }
}

@keyframes modalSlideIn {
    from {
        transform: translateY(-50px);
        opacity: 0;
    }
    to {
        transform: translateY(0);
        opacity: 1;
    }
}

@keyframes slideDown {
    from {
        transform: translate(-50%, -100%);
        opacity: 0;
    }
    to {
        transform: translate(-50%, 0);
        opacity: 1;
    }
}

/* ===== Facilitator Screens ===== */
.facilitator-info {
    margin: var(--spacing-xl) 0;
}

.game-code-large {
    font-size: var(--font-size-xlarge);
    font-weight: var(--font-weight-bold);
    color: var(--color-primary);
    padding: var(--spacing-sm) var(--spacing-lg);
    background: var(--color-primary-light);
    border-radius: var(--radius-pill);
    display: inline-block;
}

.share-link-box {
    margin: var(--spacing-lg) 0;
    padding: var(--spacing-lg);
    background: var(--color-background);
    border-radius: var(--radius-md);
    border: 2px solid var(--color-border);
}

.share-link-container {
    display: flex;
    gap: var(--spacing-sm);
    margin-top: var(--spacing-md);
}

.share-link-input {
    flex: 1;
    padding: var(--spacing-md);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-family: monospace;
    font-size: var(--font-size-small);
    background: white;
}

.facilitator-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--spacing-md);
    padding-bottom: var(--spacing-sm);
    border-bottom: 2px solid var(--color-border);
    margin-top: 0;
}

.facilitator-header h1 {
    margin-top: 0;
    margin-bottom: 0;
    font-size: var(--font-size-large);
}

#facilitator-game-screen .container {
    padding-top: var(--spacing-sm);
    padding-bottom: var(--spacing-md);
}

#facilitator-lobby-screen .container {
    padding-top: var(--spacing-sm);
    padding-bottom: var(--spacing-md);
}

/* Remove top margins from first elements in facilitator screens */
#facilitator-lobby-screen .title {
    margin-top: 0;
    margin-bottom: var(--spacing-md);
}

#facilitator-lobby-screen h1,
#facilitator-lobby-screen h2 {
    margin-top: 0;
}

#facilitator-lobby-screen .facilitator-info {
    margin-top: var(--spacing-md);
    margin-bottom: var(--spacing-lg);
}

#facilitator-lobby-screen .facilitator-info h2 {
    margin-top: 0;
}

#facilitator-game-screen .scenario-card {
    margin-bottom: var(--spacing-md);
}

#facilitator-game-screen .scenario-card h2 {
    font-size: var(--font-size-base);
    padding: var(--spacing-md) var(--spacing-lg) var(--spacing-sm);
}

#facilitator-game-screen .scenario-card p {
    font-size: var(--font-size-small);
    padding: var(--spacing-sm) var(--spacing-lg) var(--spacing-md);
}

#facilitator-game-screen .scenario-card .scenario-icons {
    padding: 0 var(--spacing-lg) var(--spacing-md);
}

.game-info {
    display: flex;
    gap: var(--spacing-lg);
    font-size: var(--font-size-small);
}

#fac-reveal-section {
    margin-top: var(--spacing-lg);
}

#fac-reveal-section h3 {
    margin-bottom: var(--spacing-md);
}

#fac-reveal-section p {
    margin-bottom: var(--spacing-lg);
    font-size: var(--font-size-large);
}

#fac-winning-card-display {
    margin-top: var(--spacing-lg);
}

.player-hands-section {
    margin-top: var(--spacing-xl);
    padding-top: var(--spacing-xl);
    border-top: 2px solid var(--color-border);
}

.player-hand-section {
    margin-bottom: var(--spacing-lg);
    padding: var(--spacing-md);
    background: var(--color-background);
    border-radius: var(--radius-md);
}

.player-hand-section h4 {
    margin-bottom: var(--spacing-md);
    color: var(--color-text);
}

.card-hand--small {
    gap: var(--spacing-sm);
}

.card--small {
    width: 172px;
    height: 300px;             /* fixed height → every facilitator card is the same size */
    min-height: 0;
    font-size: 11.5px;
}

.card--small .card__name {
    font-size: 12px;           /* a touch smaller so longer names still fit two lines */
    height: auto;
    min-height: 32px;
    line-height: 1.2;
}

.card--small .card__image {
    height: 150px;
}

.card--small .chip {
    font-size: 9px;
    padding: 1px 6px;
}

/* Facilitator-only: a player's submitted card during SUBMIT, shown alongside
 * their remaining hand with a green border so the facilitator can prepare for
 * discussion before the round transitions to JUDGE. */
.card--submitted {
    border: 2px solid var(--color-success);
    box-shadow: 0 0 0 2px rgba(47, 143, 114, 0.25);
    position: relative;
}

.card--submitted::after {
    content: 'Submitted';
    position: absolute;
    top: -8px;
    right: -4px;
    background: var(--color-success);
    color: white;
    font-size: 10px;
    font-weight: 600;
    padding: 2px 6px;
    border-radius: var(--radius-pill);
    letter-spacing: 0.03em;
}

/* Facilitator phase-timer widget: live MM:SS countdown + "Time per turn"
 * dropdown. Visible during SUBMIT/JUDGE only. */
.phase-timer {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 4px 12px;
    background: var(--color-primary-light);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-small);
}

.phase-timer__countdown {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-weight: 700;
    font-size: 1.1em;
    min-width: 3.5em;
    text-align: center;
    color: var(--color-primary);
}

.phase-timer__countdown--warning {
    color: var(--color-warning);
}

.phase-timer__countdown--urgent {
    color: var(--color-error);
    animation: phase-timer-pulse 1s ease-in-out infinite;
}

@keyframes phase-timer-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

.phase-timer__label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--color-primary);
}

.phase-timer__label-text {
    font-size: 0.85em;
    opacity: 0.85;
}

.phase-timer__select {
    padding: 2px 6px;
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    font-size: var(--font-size-small);
    color: var(--color-primary);
}

.facilitator-controls {
    margin-top: var(--spacing-xl);
    padding: var(--spacing-lg);
    background: var(--color-background);
    border-radius: var(--radius-md);
}

/* ===== Multi-facilitator: pill, top bar, games list ===== */
.facilitators-pill {
    display: inline-block;
    padding: 4px 12px;
    margin-left: var(--spacing-sm);
    background: var(--color-primary-light);
    color: var(--color-primary-dark);
    border-radius: var(--radius-pill);
    font-size: var(--font-size-small);
    font-weight: var(--font-weight-medium);
}

.facilitator-top-bar {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: var(--spacing-sm);
    margin-bottom: var(--spacing-md);
}

.facilitator-header-actions {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    flex-shrink: 0;
}

/* ===== Version Display ===== */
.version-display {
    position: fixed;
    bottom: 10px;
    right: 10px;
    padding: 4px 8px;
    background: rgba(128, 128, 128, 0.12);
    border-radius: 4px;
    font-size: 11px;
    color: var(--color-text-secondary);
    font-family: monospace;
    pointer-events: none;
    z-index: 9999;
}

/* ===== Responsive Design ===== */
@media (max-width: 768px) {
    .card {
        width: 180px;
        height: 252px;
    }

    .card__image {
        height: 124px;
    }

    .card__name {
        height: 48px;
    }

    .button-group {
        flex-direction: column;
    }

    .container {
        padding: var(--spacing-md);
        margin-bottom: var(--spacing-xl);
    }

    #app {
        padding: var(--spacing-sm);
        padding-top: var(--spacing-xs);
    }

    /* Drop the fixed copyright banner entirely on mobile — it crowds the
       small screen (e.g. in the lobby). */
    .site-footer {
        display: none;
    }

    .btn {
        min-height: 44px;
    }

    /* Sticky condensed mini-scenario so the challenge stays in view while
       the player swipes the hand carousel beneath it (Task 16). */
    .scenario-section {
        position: sticky;
        top: 0;
        z-index: 5;
        margin-bottom: var(--spacing-md);
        background: var(--color-background); /* so content scrolling under it is masked */
    }

    .scenario-card {
        box-shadow: var(--shadow-sm);
    }

    .scenario-heading {
        padding: var(--spacing-sm) var(--spacing-md);
        font-size: var(--font-size-base);
    }

    .scenario-heading::before { margin-bottom: 2px; }

    .scenario-icons { padding: var(--spacing-sm) var(--spacing-md) 0; }

    .scenario-text {
        padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md);
        font-size: var(--font-size-base);
    }
}

/* Touch-only swipe carousel: a narrow window with a mouse (pointer: fine)
   has no swipe gesture and the scrollbar is hidden, so it must fall back
   to the base wrapping .card-hand instead of an uncyclable horizontal track. */
@media (max-width: 768px) and (pointer: coarse) {
    .card-hand {
        display: flex;
        flex-wrap: nowrap;
        justify-content: flex-start;
        gap: var(--spacing-sm);
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scroll-padding: var(--spacing-sm);
        padding-bottom: var(--spacing-sm);
        /* hide the scrollbar chrome but keep scrollability */
        scrollbar-width: none;
    }

    .card-hand::-webkit-scrollbar { display: none; }

    .card-hand > .card {
        flex: 0 0 85%;
        max-width: 85%;
        scroll-snap-align: center;
        height: auto;           /* let width drive; keep aspect via min-height */
        min-height: 340px;
    }

    .card-hand > .card .card__image { height: 200px; }
}

@media (max-width: 380px) {
    .card {
        width: 160px;
        height: 232px;
    }

    .card__image { height: 108px; }

    .scenario-heading { padding: var(--spacing-md) var(--spacing-md) var(--spacing-sm); }

    .scenario-text { padding: var(--spacing-sm) var(--spacing-md) var(--spacing-md); }

    .chip { font-size: 11px; padding: 1px 8px; }
}

/* ===== Screen Management ===== */
.screen {
    animation: fadeIn 0.3s ease-in;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* ===== Screen-reader-only utility ===== */
/* Visually hides an element while keeping it readable by assistive tech
   (used by the #phase-transition-live aria-live region). Global — not
   inside any media query. */
.visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ===== Copyright Footer ===== */
.site-footer {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: rgba(255, 255, 255, 0.95);
    border-top: 1px solid var(--color-border);
    padding: var(--spacing-sm) var(--spacing-md);
    text-align: center;
    font-size: var(--font-size-small);
    color: var(--color-text-secondary);
    z-index: 1000;
    backdrop-filter: blur(4px);
}

.site-footer p {
    margin: 0;
}

/* Add padding to app so content doesn't hide behind footer */
/* ===== Responsive Scenario + Hand Layout ===== */
.game-content {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-lg);
}

@media (min-width: 900px) {
    .game-content {
        display: grid;
        grid-template-columns: minmax(0, 33.333%) 1fr;
        grid-template-areas:
            "scenario hand"
            "feedback hand";
        gap: var(--spacing-lg);
        align-items: start;
    }

    #scenario-section { grid-area: scenario; }
    .hand-section    { grid-area: hand; }
    #submission-feedback { grid-area: feedback; }

    .scenario-section {
        max-width: 450px;
        position: sticky;
        top: var(--spacing-lg);
        /* Above the card-back logos, whose opacity creates a stacking context. */
        z-index: 5;
    }

    .hand-section {
        flex: 1;
        min-width: 0; /* Allow flex item to shrink */
    }

    .judging-section {
        flex: 1;
        min-width: 0;
    }
}

/* ===== Reveal Content ===== */
.reveal-content {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-md);
}

.reveal-content > * {
    margin: 0;
}

/* Touch devices: don't let hover-lift transforms stick after a tap. */
@media (hover: none) {
    .btn:hover { transform: none; box-shadow: none; }
    .card--selectable:hover { transform: none; box-shadow: var(--shadow-md); border-color: var(--color-border); }
}

/* ===== Accessibility & display preferences panel ===== */
.a11y-toggle {
    position: fixed;
    top: 12px;
    right: 12px;
    z-index: 1000;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-pill, 999px);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-md);
    cursor: pointer;
    font-size: 22px;
    line-height: 1;
}
.a11y-toggle:hover { border-color: var(--color-primary); }
.a11y-toggle:focus-visible {
    outline: 3px solid var(--color-primary);
    outline-offset: 2px;
}

.a11y-panel {
    position: fixed;
    top: 64px;
    right: 12px;
    z-index: 1001;
    width: 320px;
    max-width: calc(100vw - 24px);
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--spacing-lg);
}
.a11y-panel[hidden] { display: none; }

.a11y-panel__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--spacing-md);
}
.a11y-panel__title {
    font-size: var(--font-size-large);
    color: var(--color-text);
    margin: 0;
}
.a11y-panel__close {
    width: 32px;
    height: 32px;
    border: none;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: 24px;
    line-height: 1;
    cursor: pointer;
    border-radius: var(--radius-sm);
}
.a11y-panel__close:hover { color: var(--color-text); }

/* WCAG 2.5.5: 44x44 minimum touch target. .error-banner__close (~24x24,
   above) is the only way to dismiss a persistent "Lost connection" banner,
   and .a11y-panel__close is 32x32; both fall under the mobile 44px .btn
   rule because neither carries the .btn class. Placed after both selectors'
   own rules so it wins the cascade at equal specificity. (Review Client M4.) */
.error-banner__close,
.a11y-panel__close {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.a11y-group {
    border: none;
    padding: 0;
    margin: 0 0 var(--spacing-lg) 0;
}
.a11y-group__legend {
    font-size: var(--font-size-small);
    font-weight: var(--font-weight-bold);
    color: var(--color-text-secondary);
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-bottom: var(--spacing-sm);
    padding: 0;
}
.a11y-segmented {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-sm);
}
/* Real radio inputs, visually presented as segmented buttons. The input stays
   in the DOM (keyboard + screen-reader operable) but is visually hidden. */
.a11y-option {
    flex: 1 1 auto;
    min-width: 64px;
}
.a11y-option input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}
.a11y-option span {
    display: block;
    text-align: center;
    padding: 10px 12px;
    min-height: 44px;
    box-sizing: border-box;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-background);
    color: var(--color-text);
    cursor: pointer;
    line-height: 1.4;
}
.a11y-option input:checked + span {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #fff;
}
.a11y-option input:focus-visible + span {
    outline: 3px solid var(--color-primary);
    outline-offset: 2px;
}
.a11y-panel__footer {
    font-size: var(--font-size-small);
    color: var(--color-text-secondary);
    margin: 0;
}

/* Mobile: full-width bottom-sheet with comfortable touch targets. */
@media (max-width: 768px) {
    .a11y-panel {
        top: auto;
        bottom: 0;
        left: 0;
        right: 0;
        width: 100%;
        max-width: 100%;
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
        max-height: 85vh;
        overflow-y: auto;
    }
    .a11y-option span { min-height: 48px; }
}

/* Present-to-join panel (facilitator lobby) */
.present-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 1rem;
}
.present-panel {
    background: #fff;
    color: #262626;
    border-radius: 12px;
    padding: 1.5rem;
    max-width: 360px;
    width: 100%;
    text-align: center;
}
.present-panel #lobby-present-qr {
    display: block;
    width: 240px;
    height: 240px;
    margin: 1rem auto;
    image-rendering: pixelated;
}
.present-panel #lobby-present-link {
    width: 100%;
    margin-bottom: 0.75rem;
}
.present-panel__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
    margin-bottom: 0.75rem;
}

/* ===== Mobile two-axis play layout (SUBMIT + JUDGE + REVEAL) ===================
   Touch phones only. The player round/reveal screens become a fixed full-height
   frame with no page scroll: a slim header, then a vertical snap scroller
   holding two panels — the challenge (panel 1) and the card carousel / status /
   results (panel 2). Scoped to body.screen-play .play-screen so it never
   touches the other SPA screens. */
@media (max-width: 768px) and (pointer: coarse) {
    body.screen-play { min-height: 100vh; min-height: 100dvh; }  /* vh fallback first: browsers without dvh */
    body.screen-play #app { padding: 0; }
    body.screen-play .site-footer { display: none; }

    body.screen-play .play-screen .container {
        height: 100vh;  /* fallback: browsers without dvh */
        height: 100dvh;
        max-width: none;
        margin: 0;
        padding: 0;
        border-radius: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        /* Positioning context for .scroll-cards-hint below. .container itself
           does NOT scroll (overflow: hidden, fixed 100dvh) — only its child
           .game-content does — so an absolutely-positioned sibling of
           .game-content stays pinned to the visible bottom of the play area
           regardless of how far .game-content has scrolled. (Review Client
           I1.) */
        position: relative;
    }

    body.screen-play .play-screen .game-header {
        flex: 0 0 auto;
        margin: 0;
        padding: var(--spacing-sm) var(--spacing-md);
    }
    body.screen-play .play-screen .game-header .logo { display: none; }

    body.screen-play .play-screen .game-content {
        flex: 1 1 auto;
        min-height: 0;
        display: block;
        gap: 0;
        overflow-y: auto;
        scroll-snap-type: y mandatory;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
    }

    /* Panel 1 — challenge. Targets the .scenario-section CLASS so it matches
       both #round-screen (#scenario-section) and #reveal-screen. */
    body.screen-play .play-screen .scenario-section {
        position: relative;
        top: auto;
        min-height: calc(100% - 2.75rem);
        margin: 0;
        scroll-snap-align: start;
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
    }
    /* long-scenario inner scroll: cap the panel and scroll inside it, so snap
       scrolling never traps overflowing challenge text above the fold. No
       overscroll-behavior here: once the text bottoms out the swipe must
       chain to the outer snap container so the player can reach their cards
       (tester report 2026-08-12). */
    body.screen-play .play-screen .scenario-section {
        max-height: calc(100% - 2.75rem);
        overflow-y: auto;
    }
    body.screen-play .play-screen .scenario-section > :first-child { margin-top: auto; }
    body.screen-play .play-screen .scenario-section > :last-child { margin-bottom: auto; }
    body.screen-play .play-screen .scenario-section .scenario-card {
        box-shadow: var(--shadow-sm);
    }
    body.screen-play .play-screen .scenario-section .scenario-heading,
    body.screen-play .play-screen .scenario-section #reveal-scenario-heading {
        font-size: var(--font-size-large);
    }
    body.screen-play .play-screen .scenario-section .scenario-text,
    body.screen-play .play-screen .scenario-section #reveal-scenario-text {
        font-size: 20px;
    }

    /* Panel 2 — hand / judging pool / status / reveal results: full-height page. */
    body.screen-play .play-screen .hand-section {
        min-height: 100%;
        scroll-snap-align: start;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: var(--spacing-md);
    }
    /* Reveal results flow from the top and scroll internally if tall, so the
       winner + scoreboard + action buttons stay reachable. */
    body.screen-play .play-screen .hand-section.reveal-content {
        justify-content: flex-start;
    }
    body.screen-play .play-screen #player-hand-section > h3 { display: none; }

    body.screen-play .play-screen .card-hand > .card {
        min-height: 58vh;  /* fallback: browsers without dvh */
        min-height: 58dvh;
    }
    body.screen-play .play-screen .card-hand > .card .card__image {
        height: 240px;
    }

    /* Panel 3 (SUBMIT only) — "Other players" submission status, reachable by
       one more swipe down. Empty/hidden by the render logic outside SUBMIT, so
       no third panel appears in JUDGE/REVEAL. */
    body.screen-play .play-screen #submission-feedback {
        min-height: 100%;
        scroll-snap-align: start;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: var(--spacing-md);
        margin: 0;
    }
}

/* Downward "swipe for cards" hint — only rendered in the mobile two-axis layout. */
.scroll-cards-hint { display: none; }

/* Phase-transition cues (new-round ribbon + judge chip) — desktop-inert.
   JS toggles their `hidden` attribute on every viewport, so without this
   global base they'd surface unstyled on desktop. Only the mobile media
   query below reveals them. */
.new-round-ribbon,
.judge-role-chip { display: none; }

@keyframes hintBounce {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(5px); }
}

@media (max-width: 768px) and (pointer: coarse) {
    /* A tappable pill, not a whisper: testers missed the old small, low,
       decorative hint. Tapping it snaps the play area down (bound in
       _syncScrollHint), so there is always a non-gesture path to the cards.
       A direct child of .container (a sibling of .game-content), NOT of
       .scenario-section — .scenario-section is itself a scroll container
       (see the inner-scroll rules above), and for an absolutely-positioned
       child of a scroller `bottom` resolves against the full scrollable
       content, not the visible viewport. That put the hint below the fold
       for any scenario long enough to overflow, and every scenario at the
       Large/Larger text-size preference — removing the guaranteed
       non-gesture route to the cards for exactly the users who most need
       it. .container does not scroll (it's a fixed 100dvh flex frame with
       overflow: hidden — see above), so positioning against it keeps the
       hint pinned to the visible bottom of the play area regardless of how
       far .game-content has scrolled. (Review Client I1.) */
    body.screen-play .play-screen .container > .scroll-cards-hint {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        gap: 8px;
        position: absolute;
        left: 0;
        right: 0;
        width: max-content;
        margin: 0 auto;
        bottom: var(--spacing-xl);
        z-index: 5;
        padding: 12px 22px;
        border: none;
        border-radius: 999px;
        background: var(--color-primary-fill);
        color: white;
        font: inherit;
        font-size: var(--font-size-base);
        font-weight: var(--font-weight-medium);
        box-shadow: var(--shadow-md);
        cursor: pointer;
        pointer-events: auto;
        animation: hintBounce 1.6s ease-in-out infinite;
        transition: opacity 0.25s ease;
    }
    :root[data-theme="high-contrast"] body.screen-play .play-screen .container > .scroll-cards-hint {
        background: #ffdd00;
        color: #000000;
    }
    body.screen-play .play-screen .scroll-cards-hint__chevron {
        font-size: 22px;
        line-height: 1;
    }
    body.screen-play.cards-in-view .play-screen .scroll-cards-hint {
        opacity: 0;
        pointer-events: none;
    }
    /* Hide when JS marks it irrelevant; the extra [hidden] keeps it strictly
       above the show-rule on specificity. */
    body.screen-play .play-screen .container > .scroll-cards-hint[hidden] {
        display: none;
    }

    /* --- Mobile phase-transition cues --- */
    body.screen-play .play-screen #scenario-section { position: relative; }

    body.screen-play .play-screen .new-round-ribbon {
        display: block;
        position: absolute;
        top: 8px;
        left: 50%;
        transform: translateX(-50%);
        z-index: 5;
        padding: 6px 14px;
        border-radius: 999px;
        font-weight: var(--font-weight-bold);
        font-size: var(--font-size-small);
        background: var(--color-primary);
        color: #fff;
        box-shadow: 0 4px 14px rgba(0,0,0,0.25);
        animation: newRoundRibbonFade 2s ease forwards;
    }
    body.screen-play .play-screen .new-round-ribbon--judge {
        background: var(--color-warning);   /* amber accent for the judge variant */
        color: #fff;   /* was #1a1a1a: the darkened AA warning fails black text (2.94:1) */
    }

    body.screen-play .play-screen .judge-role-chip {
        display: block;
        position: absolute;
        top: 8px;
        right: 8px;
        z-index: 4;
        padding: 4px 10px;
        border-radius: 999px;
        font-size: var(--font-size-small);
        font-weight: var(--font-weight-bold);
        background: var(--color-warning);
        color: #fff;   /* was #1a1a1a: the darkened AA warning fails black text (2.94:1) */
    }

    /* Final-review wave I1: high-contrast maps --color-warning onto the
       theme's single amber (#ffdd00), where the white ink above measures
       1.35:1 — in the theme a low-vision user chose BECAUSE of their vision.
       The override has to carry the full `body.screen-play .play-screen`
       prefix and live INSIDE this media query: a plain
       `:root[data-theme="high-contrast"] .judge-role-chip` (0,2,0) loses to
       the show-rules above (0,2,1) — the same specificity trap as the
       [hidden] rules below. Black on #ffdd00 measures 15.59:1. */
    :root[data-theme="high-contrast"] body.screen-play .play-screen .new-round-ribbon--judge,
    :root[data-theme="high-contrast"] body.screen-play .play-screen .judge-role-chip {
        color: #000000;
    }

    /* Hide when JS sets the `hidden` attribute; the extra [hidden] keeps
       these strictly above the show-rules on specificity (author
       `display:block` would otherwise beat the UA `[hidden]` rule — same
       trap as the .scroll-cards-hint override above). */
    body.screen-play .play-screen .new-round-ribbon[hidden],
    body.screen-play .play-screen .judge-role-chip[hidden] {
        display: none;
    }

    body.screen-play .play-screen .scenario-card.challenge-enter {
        animation: challengeEnter 400ms ease both;
    }

    @keyframes newRoundRibbonFade {
        0%   { opacity: 0; transform: translateX(-50%) translateY(-6px); }
        12%  { opacity: 1; transform: translateX(-50%) translateY(0); }
        75%  { opacity: 1; }
        100% { opacity: 0; }
    }
    @keyframes challengeEnter {
        from { opacity: 0; transform: translateY(10px) scale(0.98); }
        to   { opacity: 1; transform: translateY(0) scale(1); }
    }

    /* Reduced motion: no slide/fade motion — the ribbon still appears
       statically (JS hides it on a 2s timer regardless). */
    @media (prefers-reduced-motion: reduce) {
        body.screen-play .play-screen .new-round-ribbon { animation: none; }
        body.screen-play .play-screen .scenario-card.challenge-enter { animation: none; }
    }
}
