h1:focus {
    outline: none;
}

/* ── Shared bar height token ────────────────────────────────────── */
/*    The bar's FULL height: 56px (the v2 bottom bar) plus the       */
/*    home-indicator inset                                           */
/*    (viewport-fit=cover in index.html makes the inset real). Pages */
/*    that reserve the bar use the token alone; never add the inset  */
/*    to it again.                                                   */
:root {
    --bottom-bar-height: calc(56px + env(safe-area-inset-bottom, 0px));
}

/* ── The theme switch's cross-fade (Settings; js/theme.js) ──────── */
/*    For the change only: every colour eases over 150 ms, then the   */
/*    class goes. Elements whose Tailwind transition utility is       */
/*    !important in its layer keep their own transition (a layered    */
/*    !important beats this one).                                      */
html.theme-fading,
html.theme-fading *,
html.theme-fading *::before,
html.theme-fading *::after {
    transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease, fill 150ms ease,
                stroke 150ms ease, box-shadow 150ms ease !important;
}

/* ── Rules that hide an element styled by Tailwind utilities ─────── */
/*    tailwindInput.css imports Tailwind with `important`, so every   */
/*    utility (hidden, md:flex, inline-flex, flex…) is !important     */
/*    inside @layer utilities. An unlayered rule loses to it, and so  */
/*    does an unlayered !important: layered !important beats it. So  */
/*    these sit in the same layer, !important, and win on            */
/*    specificity (body.x .y = 0,2,1 against a utility's 0,1,0).      */
@layer utilities {
    /* Mobile-only swap: the shell's bottom bar ↔ the page's game-status-bar (QuickPlay / Matchmaking), < 768px; the */
    /* shell hides its bottom bar from md itself.                                                                    */
    @media (max-width: 767px) {
        body:not(.game-active) .game-status-bar { display: none !important; }
        body.game-active .fm-shell__bottom      { display: none !important; }
    }

    /* Playing: nothing but the game (CLAUDE.md §15), at every width: no top bar or its clearance, no bottom bar.      */
    /* Only <InGame> (GameChrome) sets the class.                                                                      */
    body.game-playing .fm-shell__top,
    body.game-playing .fm-shell__bottom { display: none !important; }
    body.game-playing .fm-shell__main { padding-top: 0 !important; }

    /* The outage banner sits under the 60px top bar; with the bar hidden it goes to the top edge. */
    @media (min-width: 768px) {
        body.game-playing .server-banner { top: 0 !important; }
    }

    /* The language switch reloads the page, which would lose the match result (PostMatch included, where the
       top bar is back): no switch anywhere on the match screen, the top bar's language menu. */
    body.match-screen .fm-shell__lang { display: none !important; }
}

/* The boot and unhandled-error bar (redesign step 7f; screens/errors.md "boot", the prototype's): the error icon, the
   line and a Secondary small Reload, centred on surface-3 under the wrong edge, in both themes, over everything (1000,
   above the splash's 999). Blazor and js/boot.js show it by setting display: block. */
#blazor-error-ui {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1000;
    background: var(--surface-3);
    border-top: 1px solid var(--wrong);
    color: var(--ink);
    padding: var(--space-3) var(--space-4) calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
}

    #blazor-error-ui .boot-error {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: var(--space-3);
        max-width: var(--content-max);
        margin: 0 auto;
        font: 500 14px/1.4 var(--font-body);
    }

    #blazor-error-ui .boot-error__icon,
    #blazor-error-ui .fm-btn {
        flex: none;
    }
