/* ============================================================
   Latteland game pages

   Loaded after the shared design system:
       /web/latteland-site/assets/css/tokens.css
       /web/latteland-site/assets/css/site.css

   The announcement bar, header, mobile drawer, buttons, footer and
   floating buttons come from those files unchanged. This file adds
   only what the marketing site has no concept of: the game stage, the
   admin announcement ticker, the auto-play loader, and focus/embed
   modes. Every value comes from the shared token set, so these pages
   cannot drift from the rest of the site.

   Used by:
       latteland-play-ruffle.html    latteland-disco-home.html
       latteland-play-flash.html     latteland-fashion-home.html
       latteland-play-auto.html      latteland-flash-only.html
                                     latteland-ruffle-only.html
   ============================================================ */


/* ============================================================
   Page shell
   ============================================================ */

body.latteland-game {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-body);
}

body.latteland-game > main {
    flex: 1 0 auto;
}

.game-section {
    padding: var(--space-sm) 0 var(--space-lg);
    border-top: 1px solid var(--panel-border);
}


/* ============================================================
   Site header behaviour

   The header is sticky site-wide. Here it hides while scrolling down
   over the game and returns on scroll up, at the top, or once past
   the stage. Class toggled by the inline handler on each page.
   ============================================================ */

body.latteland-game .site-header {
    transition:
        transform var(--dur) var(--ease),
        background-color var(--dur) var(--ease),
        border-color var(--dur) var(--ease);
}

body.latteland-game .site-header.is-hidden {
    transform: translateY(calc(-1 * var(--header-h)));
}


/* ============================================================
   Game toolbar

   .btn line-height is pinned because <button> does not inherit it
   the way <a> does, so a ghost link and a primary button in the same
   row would otherwise render at different heights.
   ============================================================ */

.game-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-md);
    flex-wrap: wrap;
    margin-bottom: var(--space-sm);
}

.game-bar__id {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

.game-bar__id h1 {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    font-weight: 700;
    line-height: var(--lh-snug);
    color: var(--cream);
}

.game-bar__actions {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    flex: 0 0 auto;
}

.game-bar__actions .btn {
    line-height: var(--lh-body);
}


/* ============================================================
   Game stage

   Sized by height, not aspect ratio: this game does not letterbox
   into a larger container, it renders more of the world, so a bigger
   stage shows more game. Width is fluid rather than capped, with a
   real gutter so the card's border and shadow read as a card.
   ============================================================ */

.game-frame {
    width: calc(100% - 2 * var(--space-md));
    height: min(820px, calc(100vh - 150px));
    height: min(820px, calc(100svh - 150px));
    min-height: 420px;
    margin: 0 auto;
    border: 1px solid var(--panel-border);
    border-radius: var(--r-lg);
    background: #000;
    overflow: hidden;
    box-shadow: var(--shadow-lg);
}

.game-frame > * {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    background: #000;
}

.game-status {
    margin: var(--space-sm) 0 0;
    color: var(--text-muted);
    font-size: var(--fs-label);
    text-align: center;
}


/* ============================================================
   Runtime-unavailable panel

   Replaces the stage when the chosen runtime cannot run, currently
   the Flash page on a browser without usable Flash. Reuses the
   stage's box so the page does not jump, which means undoing
   `.game-frame > *`, since that rule would force the contents to fill the
   box with a black background.
   ============================================================ */

/* An author display rule outranks the UA stylesheet's [hidden] { display:
   none }, so the panel has to opt back out explicitly or it shows even
   when Flash is running fine. */
.game-blocked[hidden] { display: none; }

.game-blocked {
    display: grid;
    place-items: center;
    padding: var(--space-md);
    background: var(--surface-1);
    text-align: center;
}

.game-blocked > * {
    display: block;
    width: auto;
    height: auto;
    background: none;
}

.game-blocked__inner {
    max-width: 56ch;
}

.game-blocked h2 {
    margin: 0 0 var(--space-sm);
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    color: var(--cream);
}

.game-blocked p {
    margin: 0 0 var(--space-md);
    color: var(--text-soft);
    line-height: var(--lh-body);
}

.game-blocked__actions {
    display: flex;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
}

.game-blocked__steps {
    max-width: 44ch;
    margin: var(--space-md) auto 0;
    padding-left: 1.25em;
    color: var(--text-muted);
    font-size: var(--fs-label);
    line-height: var(--lh-body);
    text-align: left;
}

.game-blocked__steps li + li {
    margin-top: 6px;
}


/* ============================================================
   Admin announcement ticker

   Distinct from the site's `.announce` carousel at the top of the
   page: this is the scrolling marquee of runtime messages set in the
   admin panel, rendered by serve_swf.py in place of the
   <!-- ANNOUNCEMENT_TICKER --> marker.

   --ticker-bg drives both the fill and the edge fades so they cannot
   drift apart. --gold-soft is translucent, which a gradient cannot
   fade from cleanly, so it is composited against the page surface
   into an opaque value where color-mix is supported.
   ============================================================ */

.notice-ticker {
    /* Half the alpha of --gold-soft. The token is tuned for small accents;
       across a full-width band on a near-black page it reads far hotter. */
    --ticker-bg: rgba(212, 175, 55, 0.07);
    position: relative;
    width: 100%;
    margin-top: var(--space-md);
    border-top: 1px solid var(--panel-border);
    border-bottom: 1px solid var(--panel-border);
    background: var(--ticker-bg);
    color: var(--cream);
    overflow: hidden;
}

@supports (background: color-mix(in srgb, red 50%, blue)) {
    .notice-ticker {
        --ticker-bg: color-mix(in srgb, var(--gold) 7%, var(--surface));
    }
}

.notice-ticker::before,
.notice-ticker::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 1;
    width: 96px;
    max-width: 12%;
    pointer-events: none;
}

.notice-ticker::before {
    left: 0;
    background: linear-gradient(90deg, var(--ticker-bg) 0%, transparent 100%);
}

.notice-ticker::after {
    right: 0;
    background: linear-gradient(270deg, var(--ticker-bg) 0%, transparent 100%);
}

.notice-ticker__track {
    display: flex;
    align-items: center;
    min-height: 52px;
    white-space: nowrap;
    animation: lattelandTicker 40s linear infinite;
}

.notice-ticker__group {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: 44px;
    padding: 0 44px;
}

.notice-ticker__group span {
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-body);
    font-weight: 600;
}

.notice-ticker__group span::before {
    content: "";
    width: 7px;
    height: 7px;
    margin-right: 12px;
    border-radius: var(--r-full);
    background: var(--gold);
    box-shadow: 0 0 0 4px var(--gold-soft);
}

@keyframes lattelandTicker {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}


/* ============================================================
   About block
   ============================================================ */

.game-about__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-md);
}

.game-about__card {
    padding: var(--space-md);
    border-radius: var(--r-lg);
}

.game-about__card h2 {
    margin: 0 0 var(--space-sm);
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    color: var(--cream);
}

.game-about__card p {
    margin: 0 0 var(--space-sm);
    color: var(--text-soft);
    line-height: var(--lh-body);
}

.game-about__card p:last-child {
    margin-bottom: 0;
}

.game-about__card p.game-about__note {
    color: var(--text-muted);
    font-size: var(--fs-label);
}

.game-about__note a {
    color: var(--gold);
    font-weight: 600;
}


/* ============================================================
   More games

   Only the other two games appear here, so the shared 3-column grid
   would leave a dead slot. Narrowed to two columns and centred, with
   the cards keeping the homepage's exact proportions.
   ============================================================ */

.game-more .games-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: calc((var(--container) - 2 * var(--space-md)) / 3 * 2 + var(--space-md));
    margin-left: auto;
    margin-right: auto;
}


/* ============================================================
   Auto-play loader (/play)

   This route only picks a runtime and forwards, which is not always
   instant. Without this it renders as a bare black screen between
   the site and the game. The manual links stay hidden for 2.5s so a
   redirect that works never flashes a choice at the player, and show
   immediately when scripting is off.
   ============================================================ */

.play-auto-body {
    min-height: 100vh;
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-body);
}

.play-auto {
    display: grid;
    place-items: center;
    min-height: 100vh;
    min-height: 100svh;
    padding: var(--space-md);
    text-align: center;
}

.play-auto__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    max-width: 46ch;
}

.play-auto__mark {
    width: 56px;
    height: 56px;
}

.play-auto__spinner {
    width: 26px;
    height: 26px;
    margin: var(--space-md) 0;
    border: 2px solid var(--panel-border);
    border-top-color: var(--gold);
    border-radius: var(--r-full);
    animation: playAutoSpin 0.8s linear infinite;
}

.play-auto h1 {
    margin: 0 0 var(--space-sm);
    font-family: var(--font-display);
    font-size: var(--fs-h3);
    font-weight: 700;
    color: var(--cream);
}

.play-auto p {
    margin: 0 0 var(--space-md);
    color: var(--text-soft);
    line-height: var(--lh-body);
}

.play-auto__actions {
    display: flex;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
    opacity: 0;
    animation: playAutoReveal 0s linear 2.5s forwards;
}

noscript + .play-auto__actions {
    opacity: 1;
    animation: none;
}

@keyframes playAutoSpin {
    to { transform: rotate(360deg); }
}

@keyframes playAutoReveal {
    to { opacity: 1; }
}


/* ============================================================
   Focus and embed modes

   Focus hides all page chrome for distraction-free play; embed is
   the same bare stage for `?embed=1`. Toggled by bindFocusToggle()
   in the shared /web/assets/site.js.
   ============================================================ */

body.latteland-game.focus-mode .announce,
body.latteland-game.focus-mode .notice-ticker,
body.latteland-game.focus-mode .site-header,
body.latteland-game.focus-mode .site-footer,
body.latteland-game.focus-mode .floaties,
body.latteland-game.focus-mode .game-bar,
body.latteland-game.focus-mode .game-about,
body.latteland-game.focus-mode .game-status,
body.latteland-game.focus-mode #games,
body.latteland-game.focus-mode #community,
body.latteland-game.embed-mode .announce,
body.latteland-game.embed-mode .notice-ticker,
body.latteland-game.embed-mode .site-header,
body.latteland-game.embed-mode .site-footer,
body.latteland-game.embed-mode .floaties,
body.latteland-game.embed-mode .game-bar,
body.latteland-game.embed-mode .game-about,
body.latteland-game.embed-mode .game-status,
body.latteland-game.embed-mode #games,
body.latteland-game.embed-mode #community {
    display: none;
}

body.latteland-game.focus-mode,
body.latteland-game.embed-mode {
    overflow: hidden;
}

body.latteland-game.focus-mode .game-section,
body.latteland-game.embed-mode .game-section {
    padding: 0;
    border-top: 0;
}

body.latteland-game.focus-mode .game-section > .container,
body.latteland-game.embed-mode .game-section > .container {
    width: 100%;
    max-width: none;
    padding: 0;
}

body.latteland-game.focus-mode .game-frame,
body.latteland-game.embed-mode .game-frame {
    width: 100vw;
    height: 100vh;
    height: 100svh;
    max-height: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
}


/* ============================================================
   Focus exit button

   The only way out of focus mode, since all chrome is hidden. Shares
   [data-action="focus-toggle"] with the toolbar's Focus button so the
   two stay in sync, and takes its appearance from .btn/.btn--ghost.
   The fill is solid rather than the ghost button's translucent one:
   it sits over game artwork that is often bright.
   ============================================================ */

.focus-exit {
    display: none;
}

body.latteland-game.focus-mode .focus-exit,
body.latteland-game.embed-mode .focus-exit {
    display: inline-flex;
    position: fixed;
    top: 20px;
    right: 20%;
    z-index: 200;
    background: var(--panel-solid);
    border-color: var(--panel-border-bright);
    white-space: nowrap;
    opacity: 0.4;
    transition: opacity var(--dur) var(--ease);
}

body.latteland-game.focus-mode .focus-exit:hover,
body.latteland-game.embed-mode .focus-exit:hover,
body.latteland-game.focus-mode .focus-exit:focus,
body.latteland-game.embed-mode .focus-exit:focus {
    opacity: 1;
}

/* Kept separate: a browser that does not know :focus-visible discards the
   whole rule it appears in, which would take the hover state with it. */
body.latteland-game.focus-mode .focus-exit:focus-visible,
body.latteland-game.embed-mode .focus-exit:focus-visible {
    opacity: 1;
}


/* ============================================================
   Narrow screens
   ============================================================ */

@media (max-width: 900px) {
    .game-about__grid,
    .game-more .games-grid {
        grid-template-columns: 1fr;
    }

    .game-more .games-grid {
        max-width: none;
    }
}

@media (max-width: 720px) {
    .game-section {
        padding: var(--space-sm) 0 var(--space-md);
    }

    .game-bar {
        align-items: flex-start;
        flex-direction: column;
        gap: 10px;
    }

    .game-frame {
        width: calc(100% - 2 * var(--space-sm));
        height: min(calc(100svh - 150px), 62svh);
        min-height: 300px;
        border-radius: var(--r-md);
    }
}


/* ============================================================
   Reduced motion
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
    body.latteland-game .site-header,
    .play-auto__spinner,
    .notice-ticker__track {
        transition: none;
        animation: none;
    }

    .play-auto__actions {
        opacity: 1;
        animation: none;
    }
}
