/* Self-hosted, latin subset, variable across the 400–500 range this theme uses.
   No network dependency: the files sit in static/fonts and are cached a day. */
@font-face {
    font-family: 'Alegreya';
    src: url('/static/fonts/alegreya.woff2') format('woff2');
    font-weight: 400 500;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Alegreya';
    src: url('/static/fonts/alegreya-italic.woff2') format('woff2');
    font-weight: 400 500;
    font-style: italic;
    font-display: swap;
}

@font-face {
    font-family: 'Grenze Gotisch';
    src: url('/static/fonts/grenze-gotisch.woff2') format('woff2');
    font-weight: 400 500;
    font-style: normal;
    font-display: swap;
}

/* The app chrome shares the game's phone/desktop boundary, 899/900px, so a
   player crossing from home into a game never sees the two disagree. The
   number's other spellings are indexed at the top of game-shell.css. */

html,
body {
    margin: 0;
}

/* The hall: one fire, one ground, one skin of grain — laid down here and
   nowhere else, so no card repeats the light source. Background layers rather
   than an overlay element, because @view-transition snapshots the whole root
   on every navigation and a fixed overlay would swim inside that snapshot. */
html {
    background:
        var(--grain),
        var(--light-hall),
        var(--surface-1);
    background-blend-mode: overlay, normal, normal;
    background-attachment: fixed, fixed, fixed;
    background-repeat: repeat, no-repeat, no-repeat;
}

/* Inherited, so this also covers SVG tap targets, where the highlight would
   otherwise flash as a box over the element's bounds. */
html {
    -webkit-tap-highlight-color: transparent;
}

/* As tall as the page's content and no taller: never stretched to the
   viewport (the normalize's min-block-size included). The background is on
   html and the bottom bar is fixed, so nothing needs the stretch, and no
   browser's idea of the viewport's height can leave a short page a few pixels
   to scroll: Chrome on a phone took 100vh as the viewport without its URL bar,
   and Chrome on iOS even 100svh as a little more than it shows, and either
   landed a page scrolled when it opened. */
body {
    min-block-size: 0;
    font-family: var(--font-body);
    font-weight: var(--fw-normal);
    color: var(--text-1);
    display: flex;
    flex-direction: column;
}

/* Shared name truncation. Needs a bounded parent (flex row with min-width:0, or
   an explicit max-width). */
.name-ellipsis {
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

h1 {
    font-family: var(--font-display);
    font-size: var(--fs-4);
    font-weight: var(--fw-normal);
    letter-spacing: 0.01em;
    margin: 0 0 var(--size-3);
}

h2 {
    font-family: var(--font-display);
    font-size: var(--fs-3);
    font-weight: var(--fw-normal);
    letter-spacing: 0.01em;
    margin: 0 0 var(--size-3);
}

.top-loader {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 100%;
    z-index: 1000;
    pointer-events: none;
    overflow: hidden;
}

/* transform (compositor-animatable) so the bar keeps moving while the main
   thread is busy loading the next page */
.top-loader::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--color-accent);
    transform: scaleX(0);
    transform-origin: left;
}

html.nav-loading .top-loader::after {
    animation: top-loader-fill 4s cubic-bezier(0.1, 0.8, 0.2, 1) forwards;
}

@keyframes top-loader-fill {
    0% { transform: scaleX(0); }
    15% { transform: scaleX(0.7); }
    100% { transform: scaleX(0.92); }
}

.site-header {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--size-2) var(--size-3);
    background: var(--surface-2);
    border-bottom: var(--rule-1);
}

.site-brand {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--size-2);
    text-decoration: none;
    min-width: 0;
}

.site-logo {
    width: 36px;
    height: 36px;
    object-fit: contain;
    flex-shrink: 0;
}

.site-brand-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.site-title {
    font-family: var(--font-display);
    font-size: var(--fs-2);
    font-weight: var(--fw-normal);
    color: var(--text-1);
    letter-spacing: 0.02em;
    line-height: 1.1;
}

@media (hover: hover) {
    .site-brand:hover .site-title {
        color: var(--link);
    }
}

.app-nav {
    display: none;
}

.app-nav-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--size-2);
    padding: var(--size-2) var(--size-3);
    border-radius: var(--radius-2);
    color: var(--text-2);
    text-decoration: none;
    font-size: var(--font-size-1);
    font-weight: var(--font-weight-6);
    transition: background 0.15s, color 0.15s;
}

@media (hover: hover) {
    .app-nav-link:hover {
        background: var(--surface-3);
        color: var(--text-1);
    }
}

.app-nav-link--active {
    background: color-mix(in srgb, var(--color-accent-bg) 22%, transparent);
    color: var(--text-1);
}

.app-nav-link .ui-icon {
    width: 1.1rem;
    height: 1.1rem;
}

.app-nav-new {
    display: inline-flex;
    border: 1px solid var(--color-accent-bg-hover);
    font-family: var(--font-display);
    cursor: pointer;
    align-items: center;
    gap: var(--size-2);
    padding: var(--size-2) var(--size-4);
    background: var(--color-accent-bg);
    color: var(--text-on-color);
    border-radius: var(--radius-1);
    text-decoration: none;
    font-size: var(--fs-1);
    font-weight: var(--fw-normal);
    letter-spacing: 0.02em;
    transition: background 0.15s;
}

@media (hover: hover) {
    .app-nav-new:hover {
        background: var(--color-accent-bg-hover);
    }
}

.app-nav-new .ui-icon {
    width: 1.1rem;
    height: 1.1rem;
}

.bottom-nav {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-around;
    gap: var(--size-2);
    padding: var(--size-2) var(--size-4);
    padding-bottom: calc(var(--size-2) + env(safe-area-inset-bottom));
    background: var(--surface-2);
    border-top: var(--rule-strong);
}

.bottom-nav-item {
    position: relative;
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    color: var(--text-3);
    text-decoration: none;
    font-size: var(--fs-0);
    font-weight: var(--fw-normal);
}

.bottom-nav-item .ui-icon {
    width: 1.4rem;
    height: 1.4rem;
}

.bottom-nav-item--active {
    color: var(--color-accent);
}

.bottom-nav-new {
    display: inline-flex;
    border: 1px solid var(--color-accent-bg-hover);
    font-family: var(--font-display);
    cursor: pointer;
    align-items: center;
    gap: var(--size-1);
    flex: 0 0 auto;
    padding: var(--size-2) var(--size-4);
    background: var(--color-accent-bg);
    color: var(--text-on-color);
    border-radius: var(--radius-1);
    text-decoration: none;
    font-size: var(--fs-1);
    font-weight: var(--fw-normal);
    letter-spacing: 0.02em;
}

@media (hover: hover) {
    .bottom-nav-new:hover {
        background: var(--color-accent-bg-hover);
    }
}

.bottom-nav-new .ui-icon {
    width: 1.1rem;
    height: 1.1rem;
}

/* Keep page content clear of the fixed bottom nav (game pages are full-bleed
   with no bottom nav, so exclude them). */
body:not(.bare-page) {
    padding-bottom: 4.75rem;
}

@media (min-width: 900px) {
    .app-nav {
        display: flex;
        align-items: center;
        gap: var(--size-2);
    }

    .bottom-nav {
        display: none;
    }

    body:not(.bare-page) {
        padding-bottom: 0;
    }
}

.ui-icon {
    width: 1.25rem;
    height: 1.25rem;
    flex-shrink: 0;
}

.entity-icon {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: var(--radius-3);
    background: color-mix(in srgb, var(--color-accent-bg) 28%, transparent);
    color: var(--color-accent-text);
}

.entity-icon .ui-icon {
    width: 1.5rem;
    height: 1.5rem;
}

/* A card's own heading. --sm is the one that sits inside a card rather than
   at the top of one. */
.card-title {
    font-size: var(--font-size-3);
    font-weight: var(--font-weight-6);
    color: var(--text-1);
    margin: 0;
}

.card-title--sm {
    font-size: var(--font-size-2);
}

.card-title-row {
    display: flex;
    align-items: center;
    gap: var(--size-2);
}

.entity-icon--sm {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--radius-round);
}

.entity-icon--sm .ui-icon {
    width: 0.9rem;
    height: 0.9rem;
}

.card-title-icon {
    display: inline-flex;
    color: var(--color-accent);
}

.card-title-icon .ui-icon {
    width: 1.15rem;
    height: 1.15rem;
}

/* ---------- Page layout ----------

   The shell owns the content column and its vertical rhythm. A non-game page
   root is `.page .page--<tier> .<pagename>`; the page class carries only
   page-specific rules — never width, never top padding. */

.content-wrapper {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: var(--size-5) var(--size-4);
}

.content-wrapper > * {
    width: 100%;
    max-width: var(--page-w-default);
}

@media (min-width: 900px) {
    .content-wrapper {
        padding: var(--size-7) var(--size-6);
    }
}

.page {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--size-5);
}

.content-wrapper > .page--narrow {
    max-width: var(--page-w-narrow);
}

.content-wrapper > .page--wide {
    max-width: var(--page-w-wide);
}

.page-header {
    display: flex;
    flex-direction: column;
    gap: var(--size-2);
}

.page-back {
    display: inline-flex;
    align-items: center;
    gap: var(--size-2);
    align-self: flex-start;
    color: var(--text-2);
    text-decoration: none;
    font-size: var(--font-size-1);
    font-weight: var(--font-weight-6);
}

@media (hover: hover) {
    .page-back:hover {
        color: var(--text-1);
    }
}

.page-back .ui-icon {
    width: 1.1rem;
    height: 1.1rem;
}

.page-header-main {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--size-3);
    min-width: 0;
}

.page-title {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--fs-page-title);
    font-weight: var(--fw-normal);
    letter-spacing: 0.01em;
    color: var(--text-1);
    min-width: 0;
}

.page-header-actions {
    display: flex;
    align-items: center;
    gap: var(--size-2);
    flex-shrink: 0;
}

.page-subtitle {
    margin: 0;
    font-size: var(--fs-1);
    font-style: italic;
    color: var(--text-2);
}

.card {
    background: var(--surface-2);
    border: var(--rule-1);
    border-radius: var(--radius-2);
    box-shadow: var(--bevel);
    padding: var(--size-4) var(--size-5);
}

.card--pad-lg {
    padding: var(--size-5);
}

.card--flush {
    padding: 0;
    overflow: hidden;
}

.empty-state {
    font-size: var(--fs-1);
    font-style: italic;
    color: var(--text-2);
    margin: 0;
}

/* Wide content scrolls inside its own box so the page body never does. */
.table-scroll {
    overflow-x: auto;
}

/* The shared table skeleton. Chrome comes from the .card around it and
   .table-scroll keeps a wide one from scrolling the page body, so this owns
   only the rules and the head. The modifiers are the three differences the
   app actually has; a fourth table should reuse one, not add a fifth. */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--font-size-1);
}

.data-table th,
.data-table td {
    text-align: left;
    padding: var(--size-2);
    border-bottom: var(--border-size-1) solid var(--border-1);
}

.data-table th {
    font-size: var(--font-size-0);
    font-weight: var(--font-weight-7);
    color: var(--text-2);
}

/* Wider gutters when the table is the whole card rather than a panel in one. */
.data-table--roomy th,
.data-table--roomy td {
    padding: var(--size-2) var(--size-3);
}

/* A table nested inside a row of another one. */
.data-table--tight th,
.data-table--tight td {
    padding: var(--size-1) var(--size-2);
}

/* A head that carries by letterform rather than by weight. */
.data-table--caps th {
    font-weight: var(--font-weight-4);
    text-transform: uppercase;
    letter-spacing: var(--font-letterspacing-3);
}

/* No trailing rule where the card edge already draws one. */
.data-table--flush-end tbody tr:last-child td {
    border-bottom: none;
}

.list-row {
    display: flex;
    align-items: center;
    gap: var(--size-3);
    min-width: 0;
}

.list-row--divided {
    padding-block: var(--size-3);
    border-bottom: var(--rule-1);
}

.list-row--divided:last-child {
    border-bottom: none;
}

button,
.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--size-2);
    padding: var(--size-2) var(--size-5);
    background: var(--color-accent-bg);
    color: var(--text-on-color);
    border: none;
    border-radius: var(--radius-2);
    font-size: var(--font-size-2);
    font-weight: var(--font-weight-6);
    cursor: pointer;
    transition: background 0.15s;
}

@media (hover: hover) {
    button:not(:disabled):hover,
    .btn:not(:disabled):hover {
        background-image: var(--hover-veil);
    }
}

button:not(:disabled):active,
.btn:not(:disabled):active {
    background-image: var(--hover-veil);
}

button:disabled,
.btn:disabled {
    background: var(--surface-4);
    color: var(--text-2);
    cursor: not-allowed;
}

@media (hover: hover) {
    button:disabled:hover,
    .btn:disabled:hover {
        background: var(--surface-4);
    }
}

.game-page {
    max-width: var(--page-w-game);
}

/* A button that takes the full width of its column, capped so it still clears
   the activity drawer's floating tab. */
.btn--block {
    display: flex;
    justify-content: center;
    width: 100%;
    max-width: 15rem;
    margin-inline: auto;
}

.btn-secondary {
    display: inline-flex;
    align-items: center;
    padding: var(--size-2) var(--size-5);
    background: transparent;
    color: var(--text-2);
    border: var(--border-size-1) solid var(--border-2);
    border-radius: var(--radius-2);
    font-size: var(--font-size-2);
    font-weight: var(--font-weight-6);
    cursor: pointer;
    text-decoration: none;
    transition: background 0.15s, color 0.15s;
}

@media (hover: hover) {
    .btn-secondary:hover {
        background: var(--surface-3);
        color: var(--text-1);
    }
}

/* "Set a board": a compact button with its plus, accent (.btn) or quiet
   (.btn-secondary). After both, so its size wins. */
.btn-create {
    flex-shrink: 0;
    padding: var(--size-2) var(--size-4);
    font-size: var(--font-size-0);
}

.btn-create .ui-icon {
    width: 1.1rem;
    height: 1.1rem;
}

.btn-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-destructive-bg);
    color: var(--text-on-color);
    border: none;
    border-radius: var(--radius-2);
    padding: var(--size-1) var(--size-3);
    font-size: var(--font-size-1);
    font-weight: var(--font-weight-5);
    cursor: pointer;
    transition: background 0.15s;
}

@media (hover: hover) {
    .btn-close:hover {
        background: var(--color-destructive-bg-hover);
    }
}

.btn-close:active {
    background: var(--color-destructive-bg-active);
}

.player-name-saving {
    font-size: var(--font-size-0);
    color: var(--text-2);
    visibility: hidden;
}

.player-name-saving.is-on {
    visibility: visible;
}

/* The page's one error line, over everything at the top of the screen so it
   never pushes content aside. A popover's UA style centres it on the viewport;
   this pins it to the top instead. */
.toast {
    inset: var(--size-3) 0 auto;
    width: max-content;
    max-width: calc(100% - 2 * var(--size-3));
    margin: 0 auto;
    padding: var(--size-2) var(--size-4);
    background: var(--surface-2);
    border: var(--rule-strong);
    border-inline-start: var(--border-size-3) solid var(--color-destructive);
    box-shadow: var(--bevel);
    color: var(--text-1);
    font-size: var(--font-size-1);
    text-align: center;
    cursor: pointer;
}

@media (prefers-reduced-motion: no-preference) {
    .toast {
        opacity: 0;
        transform: translateY(-0.75rem);
        transition:
            opacity 0.2s ease,
            transform 0.2s ease,
            overlay 0.2s ease allow-discrete,
            display 0.2s ease allow-discrete;
    }

    .toast:popover-open {
        opacity: 1;
        transform: none;
    }

    @starting-style {
        .toast:popover-open {
            opacity: 0;
            transform: translateY(-0.75rem);
        }
    }
}

.field-error {
    font-size: var(--font-size-0);
    font-weight: var(--font-weight-6);
    color: var(--color-destructive);
    margin: 0;
}

/* A check that answers as the field is typed into. It keeps its line while
   quiet, so the form does not move under the cursor. */
.field-error--live {
    visibility: hidden;
}

.field-error--live.is-on {
    visibility: visible;
}

/* link-button is a button that reads as inline link text. */
/* A button that reads as a link: inline in prose, or a quiet action at the end
   of a row. It inherits its type by default, so the modifiers are for the
   cases that sit outside running text. */
.link-button {
    padding: 0;
    background: none;
    border: none;
    font: inherit;
    color: var(--color-accent);
    text-decoration: underline;
    cursor: pointer;
}

.link-button--strong {
    font-weight: var(--font-weight-6);
}

.link-button--sm {
    font-size: var(--font-size-0);
}

.link-button--danger {
    color: var(--color-destructive);
}

/* modal-loader covers a popover modal while an in-modal action is in flight.
   The modal-card is position:fixed (popover default), which already makes it the
   containing block for this absolute overlay. */
.modal-loader {
    display: none;
    position: absolute;
    inset: 0;
    align-items: center;
    justify-content: center;
    background: var(--surface-2);
    border-radius: var(--radius-3);
    z-index: 1;
}

.modal-popover.is-loading > .modal-loader {
    display: flex;
}

.modal-loader svg {
    width: 3rem;
    height: 3rem;
    animation: loader-spin 0.7s linear infinite;
    color: var(--text-2);
}

@keyframes loader-spin {
    to { transform: rotate(360deg); }
}

.btn-spinner {
    width: 1em;
    height: 1em;
    animation: loader-spin 0.7s linear infinite;
}

/* --- People search (components.PeopleCombo) ---------------------------------
   A search box over a dropdown of people to pick: the lobby's invite box,
   New group's and the group panel's. The dropdown hangs over whatever is
   below rather than pushing it down, scrolls itself, and shows only while
   open with someone, or something, in it. */
.people-combo {
    position: relative;
    display: flex;
    flex-direction: column;
}

.people-options {
    display: none;
    position: absolute;
    inset-inline: 0;
    top: 100%;
    z-index: 5;
    max-height: min(40vh, 15rem);
    overflow-y: auto;
    margin: var(--size-1) 0 0;
    padding: 0;
    list-style: none;
    background: var(--surface-3);
    border: var(--rule-strong);
    border-radius: var(--radius-2);
}

.people-combo.is-open .people-options:not([popover]) {
    display: block;
}

.people-combo.is-open .people-options:not([popover]):empty {
    display: none;
}

/* Anchored (components.PeopleOptions): a popover in the top layer, as wide as the box,
   under it or, with more room there, over it. */
.people-options[popover]:popover-open {
    display: block;
    position: fixed;
    inset: auto;
    top: anchor(bottom);
    left: anchor(left);
    width: anchor-size(width);
    margin: var(--size-1) 0;
    position-try: most-block-size flip-block;
    color: inherit;
}

.people-options li {
    max-inline-size: none;
    padding: 0;
}

.people-options li + li {
    border-top: var(--rule-1);
}

.people-option {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--size-2);
    width: 100%;
    padding: var(--size-2);
    background: none;
    border: none;
    border-radius: 0;
    color: var(--text-1);
    font-size: var(--font-size-1);
    text-align: start;
    cursor: pointer;
}

.people-option:focus-visible,
.people-option:focus {
    outline: none;
    background: var(--surface-4);
}

@media (hover: hover) {
    .people-option:hover {
        background: var(--surface-4);
    }
}

.people-option.is-loading {
    opacity: 0.55;
}

.people-option-user {
    flex-shrink: 0;
    max-width: 50%;
    color: var(--text-3);
    font-size: var(--font-size-0);
    font-style: italic;
}

/* Already asked or in: listed, so a search for them is not empty, but no
   pick. */
.people-option--taken {
    color: var(--text-3);
    cursor: default;
}

.people-option-note {
    flex-shrink: 0;
    margin-inline-start: auto;
    font-size: var(--font-size-00);
    font-style: italic;
    color: var(--color-highlight-subtle-text);
    background: var(--color-highlight-subtle);
    padding: 2px var(--size-2);
    border-radius: var(--radius-2);
}

.people-none .empty-state {
    padding: var(--size-2);
}

/* --- Loading state ---------------------------------------------------------
   One class, .is-loading, is the whole loading protocol. A click adds it; the
   content the command answers with takes it off, because the server renders that
   content without it. Both faces of a control are always in the DOM and CSS picks
   between them, so no inline display survives a morph.

   Loading hides a face with visibility, never display: the hidden face keeps its
   box, so a control holds its size while it waits. scripts/csscheck.py fails a
   display:none under .is-loading. */
:where(:has(> .loader-spinner)) {
    position: relative;
}

.loader-spinner {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    visibility: hidden;
}

.is-loading > .loader-label {
    visibility: hidden;
}

.is-loading > .loader-spinner {
    visibility: visible;
}

/* A control that is already working can't be clicked again — the only
   double-submit guard the pattern needs. pointer-events inherits, which is what
   covers a whole loading panel. */
.is-loading {
    pointer-events: none;
}

/* The loading scope SubmitOverlay opens: the spinner and the controls share one
   grid cell, so the panel keeps the controls' size while it waits. */
.submit-scope {
    display: grid;
}

.submit-scope > * {
    grid-area: 1 / 1;
}

.submit-overlay {
    display: flex;
    align-items: center;
    justify-content: center;
    visibility: hidden;
}

.submit-scope.is-loading > .submit-overlay {
    visibility: visible;
}

.submit-scope.is-loading > .submit-content {
    visibility: hidden;
}

.submit-overlay .btn-spinner {
    width: 1.5rem;
    height: 1.5rem;
}

.game-error-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 60vh;
    padding: var(--size-4);
}

.game-error-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--size-3);
    width: 100%;
    max-width: 28rem;
    padding: var(--size-6) var(--size-5);
    background: var(--surface-2);
    border: var(--border-size-1) solid var(--color-destructive);
    border-radius: var(--radius-3);
}

.game-error-icon {
    width: 2.75rem;
    height: 2.75rem;
    color: var(--color-destructive);
}

.game-error-title {
    margin: 0;
    font-size: var(--font-size-4);
    font-weight: var(--font-weight-7);
    color: var(--color-destructive);
}

.game-error-msg {
    margin: 0;
    color: var(--text-2);
}

.game-error-actions {
    margin-top: var(--size-2);
}

.game-error-details {
    width: 100%;
    text-align: left;
    margin-top: var(--size-2);
}

.game-error-details summary {
    cursor: pointer;
    color: var(--text-2);
    font-size: var(--font-size-1);
    user-select: none;
}

@media (hover: hover) {
    .game-error-details summary:hover {
        color: var(--text-1);
    }
}

.game-error-detail-text {
    margin: var(--size-2) 0 0;
    padding: var(--size-3);
    max-height: 12rem;
    overflow: auto;
    background: var(--surface-1);
    border-radius: var(--radius-2);
    color: var(--text-2);
    font-family: var(--font-mono);
    font-size: var(--font-size-0);
    white-space: pre-wrap;
    word-break: break-word;
}

.game-waiting-text {
    text-align: center;
    color: var(--text-2);
    font-size: var(--font-size-1);
    padding: var(--size-3) 0;
    margin: 0;
}

.game-accent-name {
    color: var(--color-info-text);
    font-weight: var(--font-weight-6);
}

.game-hint {
    color: var(--text-2);
    font-size: var(--font-size-1);
    margin: 0 0 var(--size-2);
}

.game-end-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--size-3);
    padding: var(--size-5);
}

.game-winner-text {
    font-size: var(--font-size-6);
    font-weight: var(--font-weight-7);
    color: var(--color-highlight);
    margin: 0;
}

.modal-popover {
    margin: auto; /* center in top layer */
    background: var(--surface-2);
    color: var(--text-1);
    border: 1px solid var(--border-1);
    border-radius: var(--radius-3);
    box-shadow: var(--shadow-6);
}

.modal-popover::backdrop {
    background: var(--color-backdrop);
}

/* .modal-card stays viewport-centered via the popover UA default position:fixed —
   do not override it with position:relative (that drops it into normal flow and it
   scrolls with the page). That fixed positioning also anchors .modal-close. */
.modal-card {
    min-width: min(360px, 90vw);
    max-width: calc(100vw - 2 * var(--size-4));
    padding: var(--size-5);
}

.modal-close {
    position: absolute;
    top: var(--size-3);
    right: var(--size-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    padding: 0;
    background: transparent;
    color: var(--text-2);
    border: none;
    border-radius: var(--radius-1);
    font-size: var(--font-size-2);
    cursor: pointer;
}

@media (hover: hover) {
    .modal-close:hover {
        background: var(--surface-3);
        color: var(--text-1);
    }
}

.confirm-dialog {
    min-width: min(320px, 90vw);
    max-width: min(28rem, calc(100vw - 2 * var(--size-4)));
    padding: var(--size-5);
}

.confirm-title {
    font-size: var(--font-size-3);
    font-weight: var(--font-weight-6);
    margin: 0 0 var(--size-2);
}

.confirm-msg {
    color: var(--text-2);
    margin: 0 0 var(--size-5);
    font-size: var(--font-size-1);
}

.confirm-actions {
    display: flex;
    gap: var(--size-3);
    justify-content: flex-end;
}

/* Equal-width buttons, each with its label centered — inline-flex buttons would
   otherwise pack their text to the start once stretched. */
.confirm-actions > * {
    flex: 1;
    justify-content: center;
    text-align: center;
}

@media (prefers-reduced-motion: no-preference) {
    .modal-popover {
        opacity: 0;
        transform: scale(0.96);
        transition:
            opacity 0.18s ease,
            transform 0.18s ease,
            overlay 0.18s ease allow-discrete,
            display 0.18s ease allow-discrete;
    }

    .modal-popover:popover-open {
        opacity: 1;
        transform: scale(1);
    }

    @starting-style {
        .modal-popover:popover-open {
            opacity: 0;
            transform: scale(0.96);
        }
    }

    .modal-popover::backdrop {
        background: transparent;
        transition:
            background 0.18s ease,
            overlay 0.18s ease allow-discrete,
            display 0.18s ease allow-discrete;
    }

    .modal-popover:popover-open::backdrop {
        background: var(--color-backdrop);
    }

    @starting-style {
        .modal-popover:popover-open::backdrop {
            background: transparent;
        }
    }
}

/* Explicit anchor-name/position-anchor pairs rather than the popover's implicit
   anchor, which is no longer the initial value of position-anchor. */
[popovertarget="hud-menu"] {
    anchor-name: --hud-menu-trigger;
}

#hud-menu {
    position-anchor: --hud-menu-trigger;
}

[popovertarget="user-menu"] {
    anchor-name: --user-menu-trigger;
}

#user-menu {
    position-anchor: --user-menu-trigger;
}

.popover-menu {
    position: fixed;
    inset: unset;
    position-area: block-end span-inline-start;
    position-try-fallbacks: block-end span-inline-end, block-start span-inline-start;
    min-width: 152px;
    padding: var(--size-1);
    background: var(--surface-2);
    border: 1px solid var(--border-1);
    border-radius: var(--radius-2);
    box-shadow: var(--shadow-4);
    margin: var(--size-2) 0 0;
    flex-direction: column;
    gap: 0;
}

.popover-menu .ui-icon {
    width: 1rem;
    height: 1rem;
}

.popover-menu:popover-open {
    display: flex;
}

@media (prefers-reduced-motion: no-preference) {
    .popover-menu {
        opacity: 0;
        transform: translateY(-6px);
        transition:
            opacity 0.28s ease,
            transform 0.28s ease,
            overlay 0.28s ease allow-discrete,
            display 0.28s ease allow-discrete;
    }

    .popover-menu:popover-open {
        opacity: 1;
        transform: translateY(0);
    }

    @starting-style {
        .popover-menu:popover-open {
            opacity: 0;
            transform: translateY(-6px);
        }
    }
}

.popover-menu-item {
    display: flex;
    align-items: center;
    gap: var(--size-1);
    width: 100%;
    text-align: left;
    padding: var(--size-1) var(--size-2);
    background: transparent;
    color: var(--text-1);
    border: none;
    border-radius: var(--radius-1);
    font-size: var(--font-size-0);
    text-decoration: none;
    cursor: pointer;
}

@media (hover: hover) {
    .popover-menu-item:hover {
        background: var(--surface-3);
    }
}

.popover-menu-item.btn-close {
    justify-content: flex-start;
    background: transparent;
    color: var(--color-destructive);
}

@media (hover: hover) {
    .popover-menu-item.btn-close:hover {
        background: var(--surface-3);
    }
}

/* A menu row whose state is a switch, not a destination. The track is pushed to
   the far edge; the icon and label keep the row's normal gap. */
.popover-menu-item-switch > .ui-switch-track {
    margin-inline-start: auto;
}

/* The one switch. Two things drive it, because two things own their state
   differently: the history filter is a checkbox bound to a signal, and the map's
   display toggles are classes on <html> (see static/js/map-toggles.js) — a morph
   of the game shell would strand a checked attribute the server re-rendered. */
.ui-switch-track {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 0.9375rem;
    height: 0.9375rem;
    background: var(--surface-1);
    border: var(--rule-strong);
    border-radius: var(--radius-1);
    transition: background 0.15s ease, border-color 0.15s ease;
}

/* The mark is a tick drawn from two borders of a rotated box, so the switch
   stays two spans and reads as ink in a ruled ledger box. */
.ui-switch-knob {
    flex: 0 0 auto;
    width: 0.3125rem;
    height: 0.5625rem;
    border-right: 2px solid var(--color-accent);
    border-bottom: 2px solid var(--color-accent);
    transform: translateY(-1px) rotate(42deg);
    opacity: 0;
    transition: opacity 0.12s ease;
}

.game-history-toggle-input:checked + .ui-switch-track,
:root:not(.names-off) [data-map-toggle="names"] .ui-switch-track,
:root:not(.regions-off) [data-map-toggle="regions"] .ui-switch-track {
    border-color: var(--color-accent-muted);
}

.game-history-toggle-input:checked + .ui-switch-track .ui-switch-knob,
:root:not(.names-off) [data-map-toggle="names"] .ui-switch-knob,
:root:not(.regions-off) [data-map-toggle="regions"] .ui-switch-knob {
    opacity: 1;
}

.game-history-toggle-input:focus-visible + .ui-switch-track,
[data-map-toggle]:focus-visible .ui-switch-track {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

@view-transition {
    navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 0.2s;
}

/* The site header and the bottom bar are the same on every page, so they hold
   still through a navigation: each is its own snapshot, swapped for the new
   page's at once (a lit nav item changes, nothing moves), while only the page
   between them crossfades. Left in the root they would fade out and back with
   it, which reads as the header blinking and the page jumping under it. */
.site-header {
    view-transition-name: site-header;
}

.bottom-nav {
    view-transition-name: bottom-nav;
}

::view-transition-group(site-header),
::view-transition-group(bottom-nav),
::view-transition-old(site-header),
::view-transition-old(bottom-nav),
::view-transition-new(site-header),
::view-transition-new(bottom-nav) {
    animation: none;
}

::view-transition-old(site-header),
::view-transition-old(bottom-nav) {
    opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation: none;
    }
}

.site-header-right {
    display: flex;
    align-items: center;
    gap: var(--size-2);
}

.site-icon-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    color: var(--text-3);
}

@media (hover: hover) {
    .site-icon-link:hover {
        color: var(--text-1);
    }
}

.site-icon-link:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 1px;
}

.site-icon-link--active {
    color: var(--color-accent);
}

.site-icon-link .ui-icon {
    width: 1.15rem;
    height: 1.15rem;
}

/* Turn-alert push bell. State lives on <html> so SSE-patched copies stay in sync. */

.site-push-bell {
    display: none;
    position: relative;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    background: transparent;
    border: none;
    color: var(--text-3);
    cursor: pointer;
}

html.push-supported .site-push-bell,
html.push-ios-hint .site-push-bell {
    display: inline-flex;
}

.push-ios-steps {
    display: grid;
    gap: var(--size-2);
    margin: 0;
    padding-inline-start: var(--size-4);
    font-size: var(--font-size-1);
    color: var(--text-2);
}

@media (hover: hover) {
    .site-push-bell:hover {
        background: transparent;
        color: var(--text-1);
    }
}

.site-push-bell:focus {
    outline: none;
}

.site-push-bell:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 1px;
}

.site-push-bell .ui-icon {
    width: 1.15rem;
    height: 1.15rem;
}

html.push-on .site-push-bell {
    color: var(--color-accent);
}

.site-push-bell .btn-spinner {
    display: none;
    width: 1rem;
    height: 1rem;
}

html.push-busy .site-push-bell .ui-icon,
html.push-busy .site-push-bell .push-bell-badge {
    display: none;
}

html.push-busy .site-push-bell .btn-spinner {
    display: block;
}

.push-bell-badge {
    display: none;
    position: absolute;
    top: -3px;
    right: -3px;
    width: 0.75rem;
    height: 0.75rem;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-round);
    background: var(--color-success-bg);
    color: var(--text-on-color);
}

.push-bell-badge .ui-icon {
    width: 0.5rem;
    height: 0.5rem;
    stroke-width: 4;
}

html.push-on .site-push-bell .push-bell-badge {
    display: flex;
}
