/* ══════════════════════════════════════
   Hub page (play/index.html) overrides
   Sits on top of the shared style.css
   ══════════════════════════════════════ */

/* ── Hub page layout ── */
/* 2026-04-29 — Use `height: 100vh` (not `min-height`) so the body
   never grows past the viewport. The .hub-scale-wrap below has a
   fixed layout height of 600px (1000x600 design box). With
   `min-height: 100vh` plus a 600px-tall flex child, the body grew
   to 600px on any viewport shorter than that (e.g. landscape mobile
   at 375px) — flex then centred the wrap within the OVERSIZED body,
   not the viewport, so the visual ended up offset 113px below
   centre and the bottom of the carousel got clipped by
   `overflow: hidden`. Capping body to the viewport's actual height
   makes flex centre the wrap within what the user actually sees.

   2026-04-29 (later) — Switched to `100dvh` (dynamic viewport height)
   with a `100vh` fallback. On mobile browsers `vh` is frozen at the
   page's initial viewport size and DOESN'T update when entering
   fullscreen via the Fullscreen API or when the OS chrome hides —
   so on entering fullscreen the body stayed at the pre-fullscreen
   height and the bg layers (which also used `100vh`) left a gap at
   the bottom of the screen. `100dvh` reflects the live viewport
   size and resizes everything correctly. The `100vh` line below is
   left as a fallback for any browser too old to support dvh; modern
   browsers (Chrome 108+, Safari 15.4+, Firefox 101+) all use the
   dvh value because the cascade picks the last valid declaration. */
body {
    background: #000 !important;
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* ── Carousel container — standalone, not inside overlay ──
   2026-04-29 — Vertical centering fix for landscape mobile + desktop.
   Content (header + carousel viewport + arrows) is ~380px tall but
   the design box is 600px, so with the prior `justify-content:
   flex-start` ~190px of empty space pooled at the bottom — the body
   centered the SCALE-WRAP in the viewport, but the content within it
   was biased toward the top. Switched to `justify-content: center`
   so the content occupies the visual centre of the scaled box. The
   portrait media-query below restores `flex-start` because portrait
   uses an explicit `padding-top: 100px` to anchor the header below
   the install-button area; mixing `center` with that padding throws
   the layout off. ── */
.hub-carousel {
    position: relative;
    width: 1000px;
    height: 600px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 30px 10px 20px;
    box-sizing: border-box;
    overflow: hidden;
    font-family: "White Wood", "Woodbone", Arial, sans-serif;
}

/* ── Scale the carousel to fit viewport ── */
.hub-scale-wrap {
    position: relative;
    width: 1000px;
    height: 600px;
    transform-origin: center center;
}

/* ── 3 games fit equally in viewport ── */
.hub-carousel .uov-series-viewport {
    width: 840px;
    max-width: 840px;
    overflow: hidden;
    padding-bottom: 20px;
}
.hub-carousel .uov-series-carousel {
    overflow: visible;
}
.hub-carousel .uov-series-item {
    cursor: pointer;
    width: 264px;
}
.hub-carousel .uov-series-img-wrap {
    width: 250px;
    height: 250px;
}

/* ── Slightly larger logo for hub ── */
.hub-carousel .uov-series-logo {
    width: 240px;
}

/* ── "PLAY THE" caption — small SVG above the series logo,
       horizontally aligned with it (uov-series-header is centered).
       Sized small + tucked close to the logo per user feedback
       (2026-04-27: "smaller and nearer to the whack it series logo"). ── */
.hub-text-play-the {
    display: block;
    width: 55px;
    height: auto;
    margin: 0 auto -4px;
    pointer-events: none;
    position: relative;
    z-index: 1;
	top:10px;
}

/* ── Hub: disable series-logo hover swap ──
   The shared style.css applies a hover transition that swaps
   .uov-series-logo (default) for .uov-series-logo-hover when the
   header is hovered — that's intended for the in-game series tab
   where the logo doubles as a clickable home link. On the hub the
   logo is decorative, so the swap is unwanted (user feedback
   2026-04-27). Hide the hover variant entirely on the hub so the
   default logo stays visible regardless of hover state. */
.hub-carousel .uov-series-logo-hover {
    display: none !important;
}
.hub-carousel .uov-series-header:hover .uov-series-logo {
    opacity: 1 !important;
}

/* ── Decorative elements — full colour ── */
.hub-carousel .uov-series-bat {
    opacity: 1;
}

/* ── Whack It logo — top left, outside scaled container ── */
.hub-logo-corner {
    position: fixed;
    top: 30px;
    left: 30px;
    width: 70px;
    height: auto;
    pointer-events: none;
    z-index: 6;
}

/* ── Top-right utility buttons (PWA install + fullscreen) ──
   Markup pattern mirrors the games' #custom-menu top-left install
   icons so play/js/pwa.js can target the same #pwa-install-btn-phone
   / #pwa-install-btn-desktop IDs. Fullscreen toggle is a self-
   contained button (the games' lib/scale-game.js + lib/fullscreen-btn.js
   pair is wired to #game-wrapper / Ruffle and not appropriate here). */
.hub-top-right {
    position: fixed;
    top: 22px;
    right: 22px;
    display: flex;
    align-items: center;
    gap: 10px;
    z-index: 7;
}
.hub-top-right .pwa-install-btn {
    position: relative;
    width: 120px;     /* was 44px — bigger desktop install icon per
                        user feedback 2026-04-27 ("on desktop make the
                        add to phone icon bigger"). */
    height: 120px;
    cursor: pointer;
    display: block;
    transition: transform 0.15s;
}
.hub-top-right .pwa-install-btn:hover { transform: scale(1.08); }
.hub-top-right .pwa-install-btn img {
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    height: 100%;
    display: block;
}
.hub-top-right .pwa-install-btn .btn-hover {
    opacity: 0;
    transition: opacity 0.15s;
}
.hub-top-right .pwa-install-btn:hover .btn-hover { opacity: 1; }
.hub-top-right .pwa-install-btn:hover .btn-default { opacity: 0; }

.hub-fs-btn {
    width: 38px;
    height: 38px;
    background: transparent;
    border: 2px solid #1a1a1a;
    border-radius: 8px;
    color: #1a1a1a;
    cursor: pointer;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.15s, background 0.15s, color 0.15s, border-color 0.15s;
    /* 2026-04-29 — `touch-action: manipulation` disables the legacy
       300 ms tap-delay browsers used to apply while waiting to see
       if a tap was the start of a double-tap-to-zoom; without it,
       repeated rapid taps on the fullscreen icon felt unresponsive
       on mobile. `manipulation` keeps panning/pinch but skips the
       double-tap delay. */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}
.hub-fs-btn:hover {
    transform: scale(1.08);
    background: #aa0000;
    border-color: #aa0000;
    color: #fff;
}
.hub-fs-btn svg {
    width: 20px;
    height: 20px;
    /* pointer-events:none ensures touch taps land on the <button>
       itself rather than on the inner SVG — Safari/iOS sometimes
       targets the inner element which can short-circuit the
       button's click listener and leave fullscreen unreachable on
       phone. */
    pointer-events: none;
}
.hub-fs-btn .fs-compress { display: none; }
body.hub-fs-active .hub-fs-btn .fs-expand { display: none; }
body.hub-fs-active .hub-fs-btn .fs-compress { display: block; }
/* When running as an installed PWA the OS already runs us fullscreen — the
   in-page toggle is redundant and the document-level Fullscreen API often
   fails inside a standalone window anyway. pwa.js sets body.pwa-mode. */
body.pwa-mode .hub-fs-btn { display: none !important; }

/* ══════════════════════════════════════
   BACKGROUND LAYERS (back to front)
   z-index: 0  ripples (white circles)
   z-index: 1  shine image
   z-index: 2  black blood
   z-index: 3  carousel + corner icons
   z-index: 5  logo
   ══════════════════════════════════════ */

/* Layer 1: White ripple circles at 70% — pre-filled on load.
   2026-04-29 — `100dvh` with `100vh` fallback. The ripple layer
   (and the .menu-ripples child whose `100%` sizing inherits from
   here) need to fill the FULL fullscreen viewport when the browser
   enters fullscreen mode; using static `100vh` left a strip at the
   bottom of the screen because `vh` doesn't update on entering
   fullscreen on most mobile browsers. `100dvh` updates live with
   the viewport. */
.hub-bg-ripples {
    position: fixed !important;
    top: 0 !important; left: 0 !important;
    width: 100vw !important; height: 100vh !important;
    height: 100dvh !important;
    transform: none !important;
    overflow: hidden;
    z-index: 0;
    opacity: 0.7;
}
.hub-bg-ripples .ripple {
    border-color: #fff;
    animation-delay: -3s; /* pre-fill: all circles already mid-loop on load */
}
.hub-bg-ripples .r1 { animation-delay: -3s; }
.hub-bg-ripples .r2 { animation-delay: -2.5s; }
.hub-bg-ripples .r3 { animation-delay: -2s; }
.hub-bg-ripples .r4 { animation-delay: -1.5s; }
.hub-bg-ripples .r5 { animation-delay: -1s; }
.hub-bg-ripples .r6 { animation-delay: -0.5s; }

/* Layer 2: Shine — full cover at top.
   2026-04-29 — Same `100dvh` fix as the ripple layer above to keep
   the shine cover the full fullscreen viewport, not the frozen
   pre-fullscreen `100vh` value. */
.hub-bg-shine {
    position: fixed;
    top: 0; left: 0;
    width: 100vw; height: 100vh;
    height: 100dvh;
    background: url('../images/backgrounds/shine.png') top center / cover no-repeat;
    z-index: 1;
    pointer-events: none;
}

/* Layer 3: Black blood — centered, max 1000px */
.hub-bg-blood {
    position: fixed;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
    max-width: 1000px;
    height: auto;
    z-index: 2;
    pointer-events: none;
}

/* Layer 4: White semi-transparent overlay — ahead of blood/shine/ripples.
   2026-04-29 — `100dvh` fix as above so the fade covers the full
   fullscreen viewport. Otherwise a strip of black body-background
   showed at the bottom of the screen on entering fullscreen on
   mobile browsers (where `vh` doesn't update). */
.hub-bg-fade {
    position: fixed;
    top: 0; left: 0;
    width: 100vw; height: 100vh;
    height: 100dvh;
    background: rgba(255, 255, 255, 0.8);
    z-index: 3;
    pointer-events: none;
}

/* ── Bottom corner icons ──
   Width split into left + right so the glove can shrink without
   touching the ninja-sword (user feedback 2026-04-27: "decrease the
   size of the glove in the bottom left"). */
.hub-icon-left,
.hub-icon-right {
    position: fixed;
    bottom: 50px;
    height: auto;
    pointer-events: none;
    z-index: 5;
}
.hub-icon-left {
    left: 50px;
    width: 80px;     /* was 120px — smaller glove per user feedback */
}
.hub-icon-right {
    right: 50px;
    width: 120px;
}

/* Carousel above fade layer */
.hub-scale-wrap {
    position: relative;
    z-index: 4;
}

/* ── Game action icons (info + play circles below thumbnail) ── */
.hub-game-actions {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 14px;
    margin-top: 10px;
}
.hub-action-btn {
    position: relative;
    display: inline-block;
    width: 31px;
    height: 31px;
    cursor: pointer;
    transition: transform 0.15s;
    text-decoration: none;
}
.hub-action-btn:hover { transform: scale(1.12); }
.hub-action-btn img {
    position: absolute;
    top: 0; left: 0;
    width: 100%;
    height: 100%;
    display: block;
}
.hub-action-btn .hub-btn-hover {
    opacity: 0;
    transition: opacity 0.15s;
}
.hub-action-btn:hover .hub-btn-hover { opacity: 1; }
.hub-action-btn:hover .hub-btn-default { opacity: 0; }

.hub-action-glyph {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: #fff;
    font-family: Georgia, 'Times New Roman', serif;
    font-weight: bold;
    line-height: 1;
    pointer-events: none;
    z-index: 1;
}
.hub-glyph-info {
    font-size: 17px;
    font-style: italic;
    padding-bottom: 1px;
}
.hub-glyph-play {
    font-size: 11px;
    font-family: Arial, sans-serif;
    margin-left: 1px; /* optical centre for triangle */
}

/* ══════════════════════════════════════
   MOBILE — portrait
   ══════════════════════════════════════ */
/* ── Mobile: fewer ripples, slower animation for performance ── */
@media (max-width: 900px) {
    .hub-bg-ripples .r4,
    .hub-bg-ripples .r5,
    .hub-bg-ripples .r6 {
        display: none;
    }
    .hub-bg-ripples .ripple {
        will-change: transform, opacity;
        animation-duration: 5s;
    }
    .hub-bg-ripples .r1 { animation-delay: -5s; }
    .hub-bg-ripples .r2 { animation-delay: -3.3s; }
    .hub-bg-ripples .r3 { animation-delay: -1.6s; }
}

@media (max-width: 600px) and (orientation: portrait) {
    /* Portrait — push the entire stack toward the top so the carousel
       + logo sit higher in the viewport (user feedback 2026-04-27:
       "move the games and whack it series logo further up"; refined
       2026-04-29 with a follow-up "move it a bit further up still" —
       padding-top reduced 7vh → 3vh). */
    body {
        justify-content: flex-start;
        padding-top: 3vh;
    }

    /* Hide the Whack It corner logo on portrait (user feedback
       2026-04-27: "hide the whack it logo on portrait devices").
       Frees up the top-left slot for the phone install button. */
    .hub-logo-corner {
        display: none;
    }
    .hub-icon-left,
    .hub-icon-right {
        bottom: 20px;
    }
    .hub-icon-left  { left: 20px;  width: 50px; } /* glove shrunk further on portrait */
    .hub-icon-right { right: 20px; width: 70px; }

    /* Portrait — phone install button moves to TOP-LEFT (where the
       Whack It logo used to live). It's the primary CTA on a touch
       device's first visit, so a corner-anchored slot is more
       prominent than the right-edge cluster. Position:fixed pulls
       it out of the .hub-top-right flex flow so the fullscreen
       toggle stays alone on the right. The desktop install button
       (which would never show on a touch device) stays inside
       .hub-top-right and remains hidden by pwa.js. */
    .hub-top-right {
        top: 14px;
        right: 14px;
        gap: 8px;
    }
    .hub-top-right .pwa-install-phone {
        position: fixed;
        top: 16px;
        left: 16px;
        width: 90px;
        height: 60px;
    }
    /* Desktop install button keeps its place inside .hub-top-right
       — it's hidden on touch devices anyway, this rule is just a
       portrait-size safeguard. */
    .hub-top-right .pwa-install-desktop { width: 100px; height: 52px; }
    /* 2026-04-29 — Bumped portrait button from 28×28 → 44×44 to
       meet the iOS / Material Design minimum touch-target of 44 px
       on a side. The earlier 28 px was borderline-tappable and the
       user reported it as "stubborn" / unresponsive on phones. The
       inner SVG glyph stays small (14×14) so the icon doesn't
       overwhelm the button — the extra space goes to invisible hit
       area around the glyph, which dramatically improves tap success
       rate without changing the visual weight on the page. */
    .hub-fs-btn { width: 44px; height: 44px; border-width: 1.5px; border-radius: 8px; }
    .hub-fs-btn svg { width: 16px; height: 16px; }

    /* Shrink design container for portrait.
       2026-04-29 — Bumped from 500 to 620 because the carousel content
       (header 145 + 16-gap + carousel-row 331 + 100-top + 20-bottom
       padding = 612px) was overflowing the 500px clip box, hiding the
       bottom of the thumbnail SVG (which carries the game name as part
       of the artwork — `WHACK YOUR NEIGHBOUR` etc.) plus the action
       icons. Updated the matching JS designH below in
       `play/index.html`'s scaleCarousel() so the scale-fit math sees
       the new height. */
    .hub-scale-wrap {
        width: 450px;
        height: 620px;
    }
    .hub-carousel {
        width: 450px;
        height: 620px;
        padding-top: 60px;      /* 2026-04-29 — was 100px; reduced to
                                   60px alongside body padding-top
                                   3vh to lift the carousel further
                                   up the viewport per user feedback. */
        /* 2026-04-29 — Pin content to the top in portrait. The default
           `justify-content: center` (set above for landscape/desktop
           centering) would override the `padding-top: 100px` anchoring
           that portrait relies on for its install-button-aware layout. */
        justify-content: flex-start;
    }

    /* Smaller "PLAY THE" caption for narrower portrait widths,
       tucked tight against the logo (negative margin overlaps the
       caption slightly under the top of the logo). */
    .hub-text-play-the { width: 56px; margin: 0 auto -4px; top:8px;left:-85px;}

    /* Single game visible — JS switches VISIBLE_GAMES to 1 */
    .hub-carousel .uov-series-viewport {
        width: 264px;
        max-width: 264px;
        flex: none;
    }
    .hub-carousel .uov-series-grid {
        gap: 0;
    }
    .hub-carousel .uov-series-item {
        width: 264px;
    }
    .hub-carousel .uov-series-img-wrap {
        width: 270px;
        height: 270px;
    }
    .hub-carousel .uov-series-arrow {
        width: 56px;
        height: 56px;
        flex: none;
    }
    .hub-carousel .uov-series-logo {
        width: 170px;
        margin-bottom: 10px;
    }
    .hub-carousel .uov-series-carousel {
        gap: 12px;
        justify-content: center;
        width: auto;
    }
}

/* ══════════════════════════════════════
   MOBILE — landscape
   ══════════════════════════════════════
   Landscape mobile (typical: <=900x500). Tighten everything: smaller
   header, less vertical padding, smaller corner decorations, smaller
   utility buttons so the carousel still has room to breathe. */
@media (max-height: 500px) and (orientation: landscape) {
    .hub-logo-corner {
        width: 40px;
        top: 8px;
        left: 10px;
    }
    .hub-icon-left,
    .hub-icon-right {
        bottom: 6px;
    }
    .hub-icon-left  { left: 10px;  width: 36px; } /* glove smaller than ninja sword */
    .hub-icon-right { right: 10px; width: 50px; }

    /* Top-right utility buttons — minimal in landscape */
    .hub-top-right {
        top: 8px;
        right: 10px;
        gap: 6px;
    }
    .hub-top-right .pwa-install-btn { width: 32px; height: 32px; }
    /* 2026-04-29 — Bumped landscape-mobile button from 30×30 → 44×44
       to meet the standard 44 px touch-target minimum (iOS / Material
       Design). User reported the icon as unresponsive on phones; the
       30 px hit area was below the threshold most users can tap
       reliably. Inner SVG kept small so the visual weight matches the
       overall compact landscape-mobile UI. */
    .hub-fs-btn { width: 44px; height: 44px; border-width: 1.5px; border-radius: 8px; }
    .hub-fs-btn svg { width: 16px; height: 16px; }

    /* Tighter carousel — top padding shaved so the header doesn't
       waste vertical space the carousel needs. */
    .hub-carousel {
        padding: 6px 10px 8px;
    }
    .hub-carousel .uov-series-logo {
        width: 220px;
    }
    .hub-text-play-the {
        width: 50px;
        margin: 0 auto -3px;
    }
    /* Reduce the gap below the thumbnail action icons so the row
       fits inside the constrained landscape height. */
    .hub-game-actions {
        margin-top: 4px;
        gap: 10px;
    }
    .hub-action-btn { width: 26px; height: 26px; }
}

/* Very short landscape (e.g. 360px-tall phones) — shrink further so
   the logo + carousel + arrows all stack without overflow. */
@media (max-height: 380px) and (orientation: landscape) {
    .hub-carousel .uov-series-logo { width: 180px; }
    .hub-text-play-the { width: 54px; }
    .hub-icon-left  { width: 30px; }
    .hub-icon-right { width: 42px; }
}


@media (max-width: 400px) and (orientation: portrait) {
	.hub-carousel .uov-series-logo {width:170px;}
}

@media (min-width: 600px) {
.whackitseries .uov-series-header {margin-bottom:60px;}
}

.whackitseries h1 {margin:0;padding:0;}