/* ---------------------------------------------------------------------------
   Game detail — the rich, almost-full-screen game view: the hero/banner art, the summary, the Play-hub
   mode cards, the how-to-play / about panels, the "you might also like" rail, and the standalone page.
   Backed by GameDetailPage.razor, GameDetail.razor, UI/SeatSlider.razor, GameModeCardHumans.razor.
   --------------------------------------------------------------------------- *//* ===========================================================================
   Game detail (GameDetail.razor)
   The rich, almost-full-screen game view: a media panel, the game's summary and Play-hub start options, then
   supporting cards (how to play · about · reviews) and a "You might also like" rail. It backs two hosts — the
   per-game route (GameDetailPage), where the catalog sends a tile, and the popup a group's clubhouse frames it
   in (GameDialog). It renders on the shell canvas (a HubLayout page, outside the light .match-surface); the media/step art is tinted by the game's
   catalog accent (--game-accent / --game-accent-deep, set inline on .gd).
   =========================================================================== */

/* The modal host: the chromeless PanelModal sizing box the card fills, bounded to the viewport so a tall card
   scrolls inside itself rather than off the scrim. */
.gd-shell { max-height: 92vh; }

/* The card itself. In the popup it clips its own scroll (.gd__scroll); on the page it grows naturally. */
.gd {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--bg-elevated);
    border: 1px solid var(--border-color);
    border-radius: 1.5rem;
    box-shadow: var(--shadow-lg);
    animation: mp-pop-in 0.28s var(--ease-spring) both;
}
.gd__scroll {
    overflow-y: auto;
    padding: clamp(1.1rem, 2.4vw, 2rem);
    display: flex;
    flex-direction: column;
    gap: clamp(1rem, 2vw, 1.6rem);
}

/* Floating top-right controls (open-on-page, close) — above the scrolling content. */
.gd__toolbar {
    position: absolute;
    top: 0.9rem;
    right: 0.9rem;
    z-index: 2;
    display: flex;
    gap: 0.4rem;
}
.gd__toolbtn {
    width: 2.75rem;
    height: 2.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 9999px;
    font-size: 1.05rem;
    background: var(--bg-elevated);
    color: var(--text-secondary);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
}
.gd__toolbtn:hover { background: var(--bg-hover); color: var(--text-primary); }

/* --- Top: the game's banner -------------------------------------------------
   The art runs the full width of the column and the name sits in it, on a scrim risen from the bottom edge.
   A game's illustration is the reason a player opened this page, so it gets the width rather than a square
   box beside a column of text — and the type on top costs the composition no vertical space at all. */
.gd__hero {
    position: relative;
    isolation: isolate;
    border-radius: 1.4rem;
    overflow: hidden;
    /* Cinematic on a wide column, squarer as it narrows — a 21:9 strip on a phone would leave a letterbox with
       nothing in it. Bounded by height too, so a short laptop viewport still shows the start options. */
    aspect-ratio: 21 / 9;
    max-height: min(46dvh, 26rem);
    border: 1px solid color-mix(in srgb, var(--game-accent) 26%, var(--border-color));
    box-shadow: var(--shadow-md);
}
@media (max-width: 56rem) {
    .gd__hero { aspect-ratio: 16 / 9; max-height: 34dvh; }
}
/* The scrim: the picture darkens toward the bottom so the name and the facts read at full contrast on top of
   it, whatever the art underneath happens to be. Tinted with the game's own accent rather than neutral black,
   so the banner still belongs to the game. */
.gd__hero::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: linear-gradient(
        0deg,
        color-mix(in srgb, var(--game-accent-deep) 42%, rgba(4, 10, 20, 0.96)) 0%,
        color-mix(in srgb, var(--game-accent-deep) 30%, rgba(4, 10, 20, 0.72)) 26%,
        transparent 62%);
}

/* The one game image: the accent gradient with its emoji over a soft glow (mirrors the featured hero). */
.gd__media-main {
    position: absolute;
    inset: 0;
    background: var(--accent-tile-bg);
    display: flex;
    align-items: center;
    justify-content: center;
}
.gd__glow {
    position: absolute;
    width: 60%;
    aspect-ratio: 1;
    top: -12%;
    border-radius: 9999px;
    background: radial-gradient(circle, color-mix(in srgb, var(--game-accent) 55%, white), transparent 70%);
    filter: blur(6px);
    opacity: 0.8;
}
.gd__emoji {
    position: relative;
    font-size: clamp(3.5rem, 9vw, 6rem);
    line-height: 1;
    filter: drop-shadow(0 8px 18px rgba(0, 0, 0, 0.38));
}
/* --- Summary column --------------------------------------------------------- */
.gd__summary { display: flex; flex-direction: column; gap: 0.9rem; }
/* Title row: set into the bottom of the banner, over its scrim. The name takes the width and the favourite
   heart sits at the right edge, so one glance covers "what is this" and "do I like it". */
.gd__titlebar {
    position: absolute;
    z-index: 1;
    inset: auto 0 0 0;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.75rem;
    padding: clamp(0.9rem, 2.2vw, 1.6rem);
}
.gd__namewrap { display: flex; flex-direction: column; gap: 0.4rem; min-width: 0; }
/* On the banner the type is always light, whichever canvas the page is on: the scrim under it is dark by
   construction, so it takes the on-a-coloured-surface token rather than the page's own text colour. */
.gd__title { min-width: 0; font-size: clamp(1.8rem, 4vw, 2.9rem); color: var(--text-on-accent); line-height: 1.02; }
.gd__titlebar .gd__fav { flex: none; align-self: flex-end; height: 3rem; }
.gd__meta { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; }
.gd__stat { font-size: 0.85rem; color: rgba(255, 255, 255, 0.88); }
.gd__dot { color: rgba(255, 255, 255, 0.5); }
/* The meta row is dot-separated, so wrapping it strands a separator at the end of a line. On a phone it stops
   wrapping and drops its last item instead — the About pane below states the same fact in full. */
@media (max-width: 40rem) {
    .gd__meta { flex-wrap: nowrap; min-width: 0; }
    .gd__meta-secondary { display: none; }
    /* The row sits inside .gd__hero's hidden overflow, so anything past the edge is clipped mid-character
       rather than ellipsised. The genre chip is the one item that yields, because the About pane below
       states it in full; the player count and the duration are the facts the row exists for. */
    .gd__chip {
        flex: 0 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .gd__stat, .gd__dot { flex: none; }
}
/* The genre chip: a soft green pill (a category flag, like the reference's "ABSTRACT"). */
.gd__chip {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 0.2rem 0.6rem;
    border-radius: 9999px;
    color: var(--accent-green-ink);
    background: color-mix(in srgb, var(--accent-green) 16%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent-green) 30%, transparent);
}
/* The lead paragraph under the banner: the page's first sentence rather than a caption beside the art, so it
   takes a reading measure and a little more size than a caption would. */
.gd__blurb { margin: 0; font-size: 1.08rem; color: var(--text-secondary); line-height: 1.5; max-width: 46rem; }

/* The Play-hub action cluster. */
.gd__actions { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.2rem; }
.gd__play {
    flex: 1 1 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.7rem 1.3rem;
    border-radius: 0.85rem;
    border: none;
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-on-accent);
    background: var(--accent-blue);
    box-shadow: 0 12px 22px -14px color-mix(in srgb, var(--accent-blue) 80%, transparent);
    cursor: pointer;
    transition: transform 0.12s var(--ease-spring), filter 0.15s ease, box-shadow 0.15s ease;
}
.gd__play:hover:not(:disabled) { filter: brightness(1.06); }
.gd__play:active:not(:disabled) { transform: translateY(1px); box-shadow: 0 6px 14px -12px color-mix(in srgb, var(--accent-blue) 80%, transparent); }
.gd__play:disabled { opacity: 0.6; cursor: default; box-shadow: none; }
/* Favorite heart — frosted glass set into the banner, filling rose when set. It sits on the game's own art, so
   it takes its surface from the picture behind it rather than from either canvas's card colour. */
.gd__fav {
    flex: 0 0 auto;
    width: 3.3rem;
    border-radius: 0.85rem;
    border: 1px solid rgba(255, 255, 255, 0.28);
    background: rgba(255, 255, 255, 0.14);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    color: rgba(255, 255, 255, 0.9);
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
}
.gd__fav:hover { background: rgba(255, 255, 255, 0.22); color: var(--accent-fav); border-color: color-mix(in srgb, var(--accent-fav) 55%, transparent); }
.gd__fav--on { color: var(--accent-fav); border-color: color-mix(in srgb, var(--accent-fav) 60%, transparent); }

.gd__btn2 {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.6rem 0.95rem;
    border-radius: 0.75rem;
    font-size: 0.9rem;
    color: var(--text-primary);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    cursor: pointer;
}
.gd__btn2:hover:not(:disabled) { background: var(--bg-hover); border-color: color-mix(in srgb, var(--accent-blue) 30%, var(--border-color)); }
.gd__btn2:disabled { opacity: 0.55; cursor: default; }
.gd__btn2--wide { width: 100%; justify-content: center; }

/* ── Play-hub mode cards ──────────────────────────────────────────────────────
   The distinct starting acts (practice vs computers, play vs humans) each get their own card, laid side by side
   on a wide summary and stacking on a narrow one. Card ③'s two orthogonal axes read as segmented controls. */
.gd__modes {
    display: grid;
    /* auto-fit never drops below one track, so a bare 19rem floor is 304px wide inside the 296px column a
       320px phone leaves — and .app-main hides horizontal overflow, so the card's right edge (where
       .mode-card__sub sits) would be cut off with nothing to scroll to. min() keeps the 19rem two-up
       threshold on a wide column and lets the single track collapse to the container below it. */
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    gap: 0.9rem;
    margin-top: 0.2rem;
}
.mode-card {
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    padding: 1.15rem 1.2rem;
    border-radius: 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
    transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.mode-card:hover { box-shadow: var(--shadow-md); }
.mode-card--practice {
    border-color: color-mix(in srgb, var(--game-accent, var(--accent-blue)) 35%, var(--border-color));
    background: linear-gradient(180deg, color-mix(in srgb, var(--game-accent, var(--accent-blue)) 5%, var(--bg-card)), var(--bg-card) 42%);
}
/* Card header: a rounded accent badge for the icon, the mode name, and its qualifier pushed to the far edge. */
.mode-card__head {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid color-mix(in srgb, var(--border-color) 70%, transparent);
}
.mode-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.6rem;
    background: color-mix(in srgb, var(--game-accent, var(--accent-blue)) 14%, var(--bg-secondary));
    font-size: 1.1rem;
    line-height: 1;
}
.mode-card__title { font-size: 1.1rem; color: var(--text-primary); }
.mode-card__sub {
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

/* Segmented control for an orthogonal axis (Opponents, Tempo). */
.seg-field { display: flex; flex-direction: column; gap: 0.3rem; }
.seg-field__label { font-size: 0.8rem; font-weight: 600; color: var(--text-muted); }
.seg {
    display: inline-flex;
    padding: 0.2rem;
    gap: 0.2rem;
    border-radius: 0.7rem;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
}
.seg__btn {
    flex: 1 1 0;
    padding: 0.4rem 0.7rem;
    min-height: 2.75rem;
    border: none;
    border-radius: 0.5rem;
    background: transparent;
    color: var(--text-primary);
    font-size: 0.88rem;
    cursor: pointer;
}
.seg__btn:hover:not(.seg__btn--on) { background: var(--bg-hover); }
.seg__btn--on {
    background: var(--accent-blue);
    color: var(--text-on-accent);
    box-shadow: 0 6px 14px -8px color-mix(in srgb, var(--accent-blue) 80%, transparent);
}

/* The open-table lobby under card ③'s Anyone cell, and the Options disclosure shared by both cards. They
   reuse .lobby-* / .create-room__* from the popup section above. */
.humans-lobby { display: flex; flex-direction: column; gap: 0.35rem; }
.humans-hint { font-size: 0.85rem; color: var(--text-muted); margin: 0; }

.mode-opts { border-top: 1px solid var(--border-color); padding-top: 0.5rem; }
.mode-opts__summary { font-size: 0.85rem; font-weight: 600; color: var(--text-muted); cursor: pointer; }
.mode-opts__body { display: flex; flex-direction: column; gap: 0.45rem; margin-top: 0.5rem; }
/* Practice card's options sit open (no disclosure), so its body has no disclosure margin. */
.mode-card--practice .mode-opts__body { margin-top: 0; }

/* The Practice card's casting, expanded: one labelled select per computer seat, stacked. Each row is the same
   label-then-control shape as the collapsed single select (.create-room__stepper), so expanding the choice
   does not re-lay-out the card around it. */
.practice-cast {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}
.practice-cast__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}
/* The switch between one opponent for the table and one per seat. A quiet text action, not a button with a
   surface: it changes how the choice above is asked, and must not compete with the card's Play. It still
   carries the shared tap-target floor, since a target a thumb misses is not quiet, it is broken. */
.practice-cast__toggle {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    padding: 0 0.1rem;
    border: none;
    background: none;
    color: var(--text-muted);
    font-size: 0.78rem;
    text-align: left;
    text-decoration: underline;
    text-underline-offset: 0.15em;
    cursor: pointer;
}
.practice-cast__toggle:hover { color: var(--text-primary); }

/* The practice table, drawn. The grid makes both mode cards as tall as the taller one, and Practice — a slider
   and a select — has less to say than the humans card beside it; rather than pinning its button to the foot
   across an empty field, the seats it is about to sit fill the gap. They also make the slider legible at a
   glance: the number says four, the row shows you and three machines. */
.mode-seats {
    flex: 1 1 auto;
    align-content: center;
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    padding: 0.7rem 0;
}
.mode-seats__seat {
    display: inline-grid;
    place-items: center;
    width: 2.4rem;
    height: 2.4rem;
    border-radius: 0.7rem;
    font-size: 1rem;
    line-height: 1;
    color: var(--text-muted);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
}
/* The player's own seat is the one that is already taken, so it is the filled one. */
.mode-seats__seat--you {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--text-on-accent);
    background: color-mix(in srgb, var(--game-accent, var(--accent-blue)) 82%, var(--scrim));
    border-color: color-mix(in srgb, var(--game-accent, var(--accent-blue)) 60%, transparent);
}
/* The primary action pins to the foot of the card so both mode cards' Play buttons line up along one edge.
   Overrides .gd__play's flex-grow so the button keeps its natural height instead of stretching to fill the
   card when the grid makes it as tall as its taller sibling. */
.mode-card__play { margin-top: auto; flex: 0 0 auto; }
/* Card ③'s foot groups its trailing buttons (a secondary + the primary Play) and pins them to the card's
   bottom edge, matching where Practice's single Play button sits. */
.mode-card__foot { margin-top: auto; display: flex; flex-direction: column; gap: 0.5rem; }
.mode-card__foot .gd__play { flex: 0 0 auto; }

/* A labeled count slider (SeatSlider): "Label … value" over a full-width range track. */
.gd-slider { display: flex; flex-direction: column; gap: 0.3rem; }
.gd-slider__top { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; }
.gd-slider__val {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: color-mix(in srgb, var(--game-accent, var(--accent-blue)) 70%, var(--text-primary));
    font-size: 1rem;
}
/* Custom track + thumb (accent-themed), with the filled portion driven by the SeatSlider's --range-fill var so
   the track reads as a progress bar rather than a bare OS slider. */
.gd-range {
    --range-accent: var(--game-accent, var(--accent-blue));
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    /* The input is a tall (≥44px) hit box so the slider clears the tap-target floor; the painted track is the
       slim centered gradient strip (background-size) the thumb rides on as visual overflow. Firefox paints its
       own track/progress pseudo-elements over this background, so the strip is the webkit/Blink rendering. */
    height: 2.75rem;
    margin: 0;
    border-radius: 9999px;
    background: linear-gradient(to right,
        var(--range-accent) 0 var(--range-fill, 50%),
        var(--bg-secondary) var(--range-fill, 50%) 100%);
    background-size: 100% 0.5rem;
    background-position: center;
    background-repeat: no-repeat;
    cursor: pointer;
    /* Own the touch gesture so a thumb drag isn't handed to a scrolling ancestor (the dialog's scroller or the
       backdrop). `none` is the standard for a value slider; scoped to the track only, so the dialog still pans
       vertically when the gesture starts off the slider. */
    touch-action: none;
}
.gd-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 1.2rem;
    height: 1.2rem;
    border-radius: 50%;
    background: var(--bg-card);
    border: 2px solid var(--range-accent);
    box-shadow: 0 2px 6px -1px rgba(0, 0, 0, 0.28);
    cursor: pointer;
    transition: transform 0.12s var(--ease-spring), box-shadow 0.15s ease;
}
.gd-range::-moz-range-thumb {
    width: 1.2rem;
    height: 1.2rem;
    border: 2px solid var(--range-accent);
    border-radius: 50%;
    background: var(--bg-card);
    box-shadow: 0 2px 6px -1px rgba(0, 0, 0, 0.28);
    cursor: pointer;
}
/* Firefox paints its own track over the input background, so the fill gradient can't show through — give it
   the track/progress pair instead (the progress is the accent-filled portion). */
.gd-range::-moz-range-track { height: 0.5rem; border-radius: 9999px; background: var(--bg-secondary); }
.gd-range::-moz-range-progress { height: 0.5rem; border-radius: 9999px; background: var(--range-accent); }
.gd-range:hover::-webkit-slider-thumb { transform: scale(1.12); }
.gd-range:active::-webkit-slider-thumb { transform: scale(1.12); box-shadow: 0 0 0 6px color-mix(in srgb, var(--range-accent) 22%, transparent); }
.gd-range:focus-visible { outline: none; }
.gd-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px color-mix(in srgb, var(--range-accent) 30%, transparent); }
.gd-range:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px color-mix(in srgb, var(--range-accent) 30%, transparent); }

/* Inert social-proof strip. */
/* What is live for this game right now. It reports on the humans card's Anyone side, so it sits under that
   card's edge rather than orphaned at the left of the page below both of them. */
.gd__online {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin-top: 0.3rem;
    padding: 0.55rem 0.85rem;
    border-radius: 0.85rem;
    background: color-mix(in srgb, var(--bg-secondary) 80%, transparent);
    border: 1px solid var(--border-color);
    width: fit-content;
    align-self: flex-end;
}
/* Below the mode-card breakpoint the two cards stack, so there is no right-hand card to sit under: the strip
   takes the reading edge instead. */
@media (max-width: 56rem) {
    .gd__online { align-self: flex-start; }
}
.gd__online-dot { width: 0.55rem; height: 0.55rem; border-radius: 9999px; background: var(--accent-green); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-green) 22%, transparent); }
.gd__online-text { font-size: 0.82rem; color: var(--text-secondary); }

/* --- Supporting panels: how to play · about (two panes) --------------------- */
.gd__panels {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: clamp(0.8rem, 1.6vw, 1.2rem);
    /* Stretch the two cards to a shared height so the row reads as one band. */
    align-items: stretch;
}
@media (max-width: 56rem) {
    .gd__panels { grid-template-columns: 1fr; }
}
.gd-panel {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 1.1rem;
    padding: 1.1rem 1.2rem;
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    height: 100%;
}
.gd-panel__head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.gd-panel__title {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 700;
    font-size: 1.02rem;
    color: var(--text-primary);
}
.gd-panel__icon { font-size: 1.05rem; }
.gd-panel__action {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.8rem;
    min-height: 2.75rem;
    border-radius: 0.7rem;
    font-size: 0.82rem;
    color: var(--accent-blue-ink);
    background: color-mix(in srgb, var(--accent-blue) 9%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent-blue) 22%, transparent);
    cursor: pointer;
}
.gd-panel__action:hover:not(:disabled) { background: color-mix(in srgb, var(--accent-blue) 15%, var(--bg-card)); }
.gd-panel__action:disabled { opacity: 0.6; cursor: default; }
/* Ghost variant for an inert placeholder action (the rulebook link, not wired up yet). */
.gd-panel__action--ghost {
    color: var(--text-secondary);
    background: var(--bg-secondary);
    border-color: var(--border-color);
    cursor: default;
}
.gd-panel__action--ghost:hover { background: var(--bg-secondary); }

/* How-to-play video: a responsive 16:9 embed. */
.gd-howto__video {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: 0.8rem;
    overflow: hidden;
    background: var(--scrim);
    border: 1px solid var(--border-color);
}
.gd-howto__video iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}
/* The card's action row (Play tutorial + rulebook), pinned below the video. */
.gd-howto__actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: auto; }

/* About: a definition list of facts, value right-aligned. */
.gd-facts { margin: 0; display: flex; flex-direction: column; gap: 0.55rem; }
.gd-fact {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid color-mix(in srgb, var(--border-color) 70%, transparent);
}
.gd-fact:last-child { border-bottom: none; padding-bottom: 0; }
.gd-fact dt { font-size: 0.82rem; color: var(--text-muted); }
.gd-fact dd { margin: 0; font-size: 0.85rem; font-weight: 600; color: var(--text-primary); text-align: right; }
.gd-fact--skills { flex-direction: column; align-items: stretch; }
.gd-fact--skills dt { margin-bottom: 0.4rem; }
.gd-skills { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.gd-skill {
    font-size: 0.72rem;
    padding: 0.15rem 0.55rem;
    border-radius: 9999px;
    color: var(--accent-blue-ink);
    background: color-mix(in srgb, var(--accent-blue) 10%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent-blue) 20%, transparent);
}

/* --- You might also like ---------------------------------------------------- */
.gd__more { display: flex; flex-direction: column; gap: 0.85rem; }
.gd__more-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 0.85rem;
}
@media (max-width: 60rem) {
    .gd__more-row { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 30rem) {
    .gd__more-row { grid-template-columns: 1fr; }
}

/* Your recent matches of this game: a full-width section reusing the shared history rows. The list grows with
   the detail scroll (.gd__scroll) rather than clipping to its own inner scrollbar. */
.gd__history { display: flex; flex-direction: column; gap: 0.85rem; }
.gd__history .history-list { max-height: none; overflow: visible; margin: 0; padding: 0; }

/* --- The game's own page (GameDetailPage.razor) ------------------------------
   A hub surface, so it sits in the shell's scrolling column and inherits the shell's own ambient wash — it
   paints no background and no full-viewport layer of its own, which would cover the shell's. It contributes
   one control the chrome does not: the way back to the catalog. */
.gd-page {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}
/* The way back to the shelf, on its own row above the detail. */
.gd-page__topbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: clamp(0.6rem, 2vw, 1rem) clamp(0.9rem, 3vw, 2.5rem) 0;
}
/* The "back to the shelf this was opened from" pill. Shared by the hub surfaces that are reached from another
   hub surface — a game's page (back to the catalog), a friend's profile (back to the friends hub), a chooser page
   (back to the play home) — because the control is the same idea in each and only its label differs. Mostly a
   link, but a button where going back is an action and not a destination (the matchmaking wait, whose way back
   also stops the search); the pointer cursor is stated because a button, unlike an anchor, has none by default. */
.page-back {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.95rem;
    min-height: 2.75rem;
    border-radius: 9999px;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text-secondary);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}
.page-back:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
    border-color: color-mix(in srgb, var(--accent-blue) 30%, var(--border-color));
}
/* The body centers the detail card under the header. */
.gd-page__body {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: clamp(0.75rem, 3vw, 2.5rem);
}
/* The unknown-game box: a small centered card on a page that otherwise carries none, so the message and its
   way back read as one small thing rather than a stray line across the width. */
.gd-page__notfound {
    max-width: 20rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    text-align: center;
}
/* On the page the detail IS the page: no card of its own, no border, no shadow. The popup host needs a card
   because it floats over a scrim; the route does not, and a card there would be a box inside a box, spending a
   gutter on saying "there is a page behind this" — which the chrome already says. Its sections (the banner, the
   mode cards, the panes) carry their own surfaces, so the composition keeps its structure and gains the width.
   It is width-capped itself (no .gd-shell wrapper), grows with its content, and lets the page scroll rather
   than clipping an inner scroller. */
.gd-page .gd {
    width: 100%;
    max-width: 76rem;
    max-height: none;
    flex: 0 1 76rem;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    overflow: visible;
}
.gd-page .gd__scroll { overflow-y: visible; padding: 0; }

/* On the page the detail is the page, not a card arriving over a scrim, so the whole route — banner, mode
   cards and panels — would scale up and overshoot on every navigation. Only the popup host pops. */
.gd-page .gd { animation: none; }

@media (prefers-reduced-motion: reduce) {
    .gd { animation: none; }
}

