/**
 * play-worldmap.css — the fast-travel world map park select + diegetic HUD
 * (docs/design/park-select-world-map.md).
 *
 * Layer order: loaded AFTER play-polish.css and play-shell.css so it wins ties.
 * Matches the duel/raid visual system: gold-on-deep-dark, --surface-edge
 * panels, Fredoka chrome, class/region tints, vignette + horizon glow.
 *
 * Accessibility ground rules (§2): every park node is a single big tap target
 * (ring + name + cta in one <button>), route lines are pointer-events:none
 * decoration behind nodes, and everything honors prefers-reduced-motion.
 */

/* ============================================================
   §3 Diegetic HUD chrome — replaces the web header on play.
   Mounted by app-shell.js renderHudChrome() ONLY for registered
   logged-in players (everyone else keeps the web header).
   ============================================================ */

/* The web header dies only once the relocated chrome exists. */
body.play-app.tpq-diegetic-hud .play-header {
	display: none;
}
/* Clear the fixed HUD chrome so content never starts under it. The height is
   MEASURED into --tpq-hud-h by app-shell.js (the chrome is wrap-safe flex — on
   narrow screens the toggle wraps to a second row and the chrome grows; a fixed
   padding would overlap). Fallback covers first paint before the measure. */
body.play-app.tpq-diegetic-hud .play-main {
	padding-top: calc(var(--tpq-hud-h, 3.3rem) + 0.4rem);
}

/* Flow layout, NOT absolute centering: plate | toggle | corners share one flex
   row and wrap when they'd collide — overlap is impossible by construction. */
.tpq-hud-chrome {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 150; /* under the action bar (160) and pause overlay */
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.4rem 0.6rem;
	padding: calc(0.4rem + env(safe-area-inset-top, 0px))
		calc(0.55rem + env(safe-area-inset-right, 0px)) 0.3rem
		calc(0.55rem + env(safe-area-inset-left, 0px));
	pointer-events: none; /* only the children take taps */
}
.tpq-hud-chrome > * {
	pointer-events: auto;
}

/* Name plate (top-left): the existing .play-unit frame on a stone slab. */
.tpq-hudplate {
	display: flex;
	align-items: center;
	gap: 0.45rem;
	padding: 0.3rem 0.55rem 0.3rem 0.35rem;
	border-radius: 12px;
	border: 1px solid rgba(201, 162, 77, 0.45); /* --wow-gold */
	background:
		linear-gradient(180deg, rgba(32, 34, 43, 0.94), rgba(20, 21, 28, 0.94));
	box-shadow:
		var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 6px 18px -10px rgba(0, 0, 0, 0.85);
	max-width: min(70vw, 21rem);
}
.tpq-hudplate .play-unit {
	min-width: 0;
}
.tpq-hudplate .play-unit-name {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.tpq-hud-stamp {
	font-size: 0.56rem;
	font-weight: 800;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: rgba(110, 226, 166, 0.9);
	border: 1px solid rgba(110, 226, 166, 0.4);
	border-radius: 6px;
	padding: 0.14rem 0.32rem;
	white-space: nowrap;
}

/* Corner glyphs (top-right): menu (pause) + power. */
.tpq-hud-corner {
	margin-left: auto;
	display: flex;
	gap: 0.4rem;
}
.tpq-hud-btn {
	width: 2.15rem;
	height: 2.15rem;
	border-radius: 50%;
	border: 1px solid rgba(243, 234, 210, 0.28);
	background:
		radial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.08), transparent 60%),
		rgba(16, 14, 24, 0.92);
	box-shadow: var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 4px 12px -6px rgba(0, 0, 0, 0.8);
	color: var(--text, #e8ecf4);
	font-size: 1.05rem;
	line-height: 1;
	cursor: pointer;
	transition: transform 0.12s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.tpq-hud-btn:hover {
	transform: translateY(-2px);
	border-color: rgba(255, 224, 122, 0.6);
	box-shadow: 0 0 12px -4px rgba(241, 196, 15, 0.55);
}
.tpq-hud-btn:active {
	transform: translateY(0) scale(0.95);
}
.tpq-hud-btn--power:hover {
	border-color: rgba(255, 107, 107, 0.6);
	box-shadow: 0 0 12px -4px rgba(255, 107, 107, 0.5);
}

/* Travel-mode toggle: only meaningful on the hub (the map band). Centered in
   the REMAINING flex space; wraps to its own row when the plate + corners
   leave too little room (then --tpq-hud-h grows and content moves down). */
.tpq-hud-mode {
	display: none;
	flex: 1 1 auto;
	justify-content: center;
	min-width: 0;
}
body.play-inpark-hub-active .tpq-hud-mode,
body.play-virtual-hub-active .tpq-hud-mode {
	display: flex;
}
/* When the toggle wraps under the plate it owns the full row, centered. */
@media (max-width: 700px) {
	body.play-inpark-hub-active .tpq-hud-mode,
	body.play-virtual-hub-active .tpq-hud-mode {
		order: 3;
		flex-basis: 100%;
	}
}
.tpq-hud-mode .play-mode-bar {
	margin: 0;
}
.tpq-hud-mode .play-mode-toggle {
	display: inline-flex;
	border-radius: 999px;
	border: 1px solid rgba(201, 162, 77, 0.5);
	background: rgba(12, 14, 20, 0.88);
	box-shadow: var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 4px 14px -8px rgba(0, 0, 0, 0.85);
	overflow: hidden;
}
.tpq-hud-mode .play-mode-opt {
	border: 0;
	background: transparent;
	color: var(--text-muted, #9aa3b5);
	font-weight: 700;
	font-size: 0.72rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	padding: 0.4rem 0.8rem;
	cursor: pointer;
}
.tpq-hud-mode .play-mode-opt.active {
	color: #241a02;
	background: var(--gold-grad, linear-gradient(180deg, #ffe07a, #d99e06));
	text-shadow: none;
}
.tpq-hud-mode .play-mode-opt[disabled] {
	opacity: 0.55;
	cursor: not-allowed;
}

/* Phones: plate compacts (xp bar hides, name truncates harder). The toggle
   row + content clearance are handled by the wrap + --tpq-hud-h measure. */
@media (max-width: 560px) {
	.tpq-hudplate {
		max-width: 58vw;
		gap: 0.3rem;
	}
	.tpq-hudplate .play-unit-xp {
		display: none;
	}
}

/* ============================================================
   §2 The world map — stage, regions, nodes, routes.
   ============================================================ */

/* Section title becomes a quiet centered eyebrow over the map. */
body.play-app .vv-hub-parks .vv-hub-section-title {
	text-align: center;
	font-family: var(--font-display, "Fredoka", sans-serif);
	font-size: 0.7rem;
	font-weight: 700;
	letter-spacing: 0.28em;
	text-transform: uppercase;
	color: var(--text-muted, #9aa3b5);
}

/* The map stage: one framed diorama panel, deep dark, vignette + horizon. */
.tpq-worldmap {
	position: relative;
	border-radius: var(--radius, 14px);
	border: 1px solid rgba(243, 234, 210, 0.16);
	background:
		radial-gradient(140% 70% at 50% 0%, rgba(36, 28, 56, 0.55), transparent 62%),
		linear-gradient(180deg, rgba(14, 13, 22, 0.96), rgba(8, 7, 13, 0.98));
	box-shadow:
		var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		var(--panel-shadow, 0 22px 48px -22px rgba(0, 0, 0, 0.85));
	overflow: hidden;
	padding: 0.4rem 0 0.9rem;
}
/* Soft vignette pulls the eye to the nodes (duel-style). */
.tpq-worldmap::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	box-shadow: inset 0 0 90px 24px rgba(8, 6, 14, 0.5);
	border-radius: inherit;
}

/* ---- Region band: a scenic vista the nodes stand in, not a list row. ---- */
.tpq-region {
	position: relative;
	padding: 0.8rem 0.8rem 1.5rem;
	min-height: clamp(11rem, 26vw, 14.5rem);
	display: flex;
	flex-direction: column;
	justify-content: flex-end; /* nodes settle onto the skyline horizon */
	/* Region theme variables — overridden per data-region-theme below. */
	--region-tint: 205, 191, 154; /* --duel-neutral */
	--region-glow: 241, 196, 15;
	--region-art: none;
	--region-sky-a: rgba(26, 22, 40, 0.5);
	--region-sky-b: rgba(12, 10, 20, 0.1);
	background: linear-gradient(180deg, var(--region-sky-a), var(--region-sky-b) 78%);
}
/* Seam between stacked regions: a faint horizon line + glow. */
.tpq-region + .tpq-region {
	border-top: 1px solid rgba(243, 234, 210, 0.1);
}
.tpq-region + .tpq-region::before {
	content: "";
	position: absolute;
	top: -1px;
	left: 8%;
	right: 8%;
	height: 1px;
	background: linear-gradient(
		90deg,
		transparent,
		rgba(var(--region-glow), 0.35),
		transparent
	);
	pointer-events: none;
}

/* §4 themes — Disney warm purple-gold dusk, Universal cool teal night,
   neutral fallback. Sky gradient + skyline silhouette per region. */
.tpq-region[data-region-theme="wdw"] {
	--region-tint: 199, 125, 255; /* --duel-magic */
	--region-glow: 241, 196, 15;
	--region-art: url("/assets/worldmap/region-wdw.png");
	--region-sky-a: rgba(58, 36, 92, 0.55);
	--region-sky-b: rgba(20, 12, 36, 0.15);
}
.tpq-region[data-region-theme="uni"] {
	--region-tint: 94, 200, 168; /* --duel-wayfinder */
	--region-glow: 94, 200, 168;
	--region-art: url("/assets/worldmap/region-uni.png");
	--region-sky-a: rgba(16, 56, 66, 0.55);
	--region-sky-b: rgba(8, 22, 30, 0.15);
}

/* Skyline silhouette: REAL scenery anchored to the band's horizon (bottom),
   not a faint wash — the nodes stand in front of it like a world-select
   vista. Gracefully absent on 404 (sky gradient carries the band). */
.tpq-region-art {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background-image: var(--region-art);
	background-repeat: repeat-x;
	background-position: center bottom -8%;
	background-size: auto 88%;
	image-rendering: pixelated;
	opacity: 0.34;
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 30%);
	mask-image: linear-gradient(180deg, transparent 0%, #000 30%);
}
.tpq-region::after {
	/* tinted haze + low horizon glow, always painted (art-independent) */
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background:
		radial-gradient(
			120% 65% at 50% 0%,
			rgba(var(--region-tint), 0.12),
			transparent 60%
		),
		radial-gradient(
			90% 30% at 50% 100%,
			rgba(var(--region-glow), 0.08),
			transparent 70%
		);
}
/* Banner + nodes ride above the atmosphere layers. */
.tpq-region-banner,
.tpq-map-nodes {
	position: relative;
	z-index: 1;
}

/* ---- Region banner: ✦ ── NAME ── ✦ gold marquee ---- */
.tpq-region-banner {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.6rem;
	margin: 0.2rem 0 0.85rem;
	font-family: var(--font-display, "Fredoka", sans-serif);
	font-size: clamp(0.8rem, 2.6vw, 0.95rem);
	font-weight: 700;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--gold-bright, #ffd75e);
	text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
}
.tpq-region-banner::before,
.tpq-region-banner::after {
	content: "";
	flex: 1;
	max-width: 7.5rem;
	height: 1px;
	background: linear-gradient(90deg, transparent, rgba(241, 196, 15, 0.45));
}
.tpq-region-banner::after {
	background: linear-gradient(90deg, rgba(241, 196, 15, 0.45), transparent);
}
.tpq-region-orn {
	font-size: 0.62em;
	color: rgba(241, 196, 15, 0.85);
}
.tpq-region-name {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* ---- Node cluster: centered flex-wrap so 3 or 4 parks, 1 or 2 rows,
   single- or two-region all space evenly (§2.1/§2.2). ---- */
.tpq-map-nodes {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: flex-start;
	row-gap: 0.8rem;
}

/* ---- Route line: a glowing dotted trail between adjacent stops (board-game
   path). pointer-events none + aria-hidden; purely decorative. ---- */
.tpq-map-route {
	flex: 0 0 clamp(0.8rem, 3.4vw, 2.3rem);
	height: 4px;
	margin-top: clamp(2.15rem, 6.8vw, 2.55rem); /* ring vertical center */
	pointer-events: none;
	background-image: radial-gradient(
		circle at 2px 2px,
		rgba(255, 224, 122, 0.45) 1.5px,
		transparent 1.6px
	);
	background-size: 8px 4px;
	background-repeat: repeat-x;
	filter: drop-shadow(0 0 3px rgba(241, 196, 15, 0.35));
}

/* ---- Map node: one big tappable travel portal (ring + crest + name + cta). ---- */
.tpq-map-node {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.3rem;
	width: clamp(6.6rem, 21vw, 8.6rem);
	padding: 0.35rem 0.25rem 0.45rem;
	border: 0;
	background: transparent;
	color: var(--text, #e8ecf4);
	text-decoration: none;
	text-align: center;
	cursor: pointer;
	position: relative;
	border-radius: 12px;
	transition: transform 0.12s ease;
	-webkit-tap-highlight-color: transparent;
}
.tpq-map-node:hover {
	transform: translateY(-3px);
}
.tpq-map-node:active {
	transform: translateY(-1px) scale(0.97);
}
.tpq-map-node:focus-visible {
	outline: 2px solid rgba(255, 224, 122, 0.8);
	outline-offset: 2px;
}

/* Portal ring + crest: a lit medallion standing on the vista. */
.tpq-map-node-ring {
	display: flex;
	align-items: center;
	justify-content: center;
	width: clamp(4.3rem, 13.5vw, 5.2rem);
	aspect-ratio: 1;
	border-radius: 50%;
	border: 2px solid rgba(var(--region-tint), 0.85);
	/* Solid, lit disc — opaque enough that the crest reads, not a window
	   onto the neon scene behind it. */
	background:
		radial-gradient(
			120% 95% at 50% 0%,
			rgba(var(--region-tint), 0.6),
			transparent 62%
		),
		rgba(34, 29, 50, 1);
	/* Always-on outer glow ring separates every node from the scenery. */
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.2),
		inset 0 -5px 10px rgba(0, 0, 0, 0.5),
		0 0 0 4px rgba(8, 6, 14, 0.55),
		0 0 22px 2px rgba(var(--region-tint), 0.5),
		0 8px 18px -8px rgba(0, 0, 0, 0.9);
	transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.tpq-map-node:hover .tpq-map-node-ring {
	transform: scale(1.06);
	border-color: rgba(255, 224, 122, 0.75);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.16),
		inset 0 -5px 10px rgba(0, 0, 0, 0.55),
		0 0 22px -4px rgba(241, 196, 15, 0.6),
		0 8px 18px -8px rgba(0, 0, 0, 0.9);
}

/* Crest: pixel-art img when /assets/worldmap/crest/{pid}.png loads (has-art
   suppresses the emoji), emoji ::before as the always-safe fallback. */
.tpq-map-node-crest {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	border-radius: 50%;
	/* Solid backing plate so the pixel crest always has contrast over the
	   (now bright) scene behind the medallion. */
	background: radial-gradient(circle at 50% 42%, rgba(40, 34, 58, 0.96), rgba(20, 16, 32, 0.99) 80%);
	filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.85));
}
/* Art fills the ring: the PNGs carry their own transparent padding, so ~92%
   of the circle reads full-bleed without the corners ever clipping the rim. */
.tpq-map-node-crest-img {
	display: none;
	width: 94%;
	height: 94%;
	object-fit: contain;
	/* SMOOTH downscale, NOT pixelated: these ~384px pixel-art crests are crushed
	   into an ~80px ring, and nearest-neighbor downscaling drops pixels unevenly
	   (the jagged "weird at small sizes" look). A high-quality smooth downscale
	   reads as a clean icon. (Region/duel backdrops keep image-rendering:pixelated
	   — they're large and the pixel aesthetic works there.) */
	image-rendering: auto;
	pointer-events: none;
	/* Lift the pixel crest off the busy scene: brighter, punchier, with a
	   dark rim so light buildings keep a readable edge. */
	filter:
		brightness(1.12) contrast(1.05) saturate(1.1)
		drop-shadow(0 0 2px rgba(0, 0, 0, 0.85))
		drop-shadow(0 2px 4px rgba(0, 0, 0, 0.6));
}
.tpq-map-node-crest.has-art .tpq-map-node-crest-img {
	display: block;
}
/* [data-park-crest] keeps this above the per-park emoji content rules below
   (they tie at (0,2,0) and come later in the file — this is (0,3,0)). */
.tpq-map-node-crest.has-art[data-park-crest]::before,
.tpq-map-node-crest.has-art::before {
	content: none;
}
.tpq-map-node-crest::before {
	content: "🎡";
	font-size: clamp(1.6rem, 5.2vw, 2rem);
	line-height: 1;
}
.tpq-map-node-crest[data-park-crest="mk"]::before { content: "🏰"; }
.tpq-map-node-crest[data-park-crest="epcot"]::before { content: "🌐"; }
.tpq-map-node-crest[data-park-crest="hs"]::before { content: "🎬"; }
.tpq-map-node-crest[data-park-crest="ak"]::before { content: "🌳"; }
.tpq-map-node-crest[data-park-crest="usf"]::before { content: "🎥"; }
.tpq-map-node-crest[data-park-crest="ioa"]::before { content: "🦖"; }
.tpq-map-node-crest[data-park-crest="epu"]::before { content: "🌌"; }

/* Name plaque: a small engraved sign under the medallion (game UI, not web text). */
.tpq-map-node-name {
	font-weight: 700;
	font-size: clamp(0.72rem, 2.3vw, 0.8rem);
	line-height: 1.18;
	padding: 0.2rem 0.55rem;
	border-radius: 8px;
	border: 1px solid rgba(255, 255, 255, 0.12);
	background: rgba(10, 8, 16, 0.78);
	box-shadow: var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 3px 8px -4px rgba(0, 0, 0, 0.8);
}
.tpq-map-node-cta {
	font-size: 0.6rem;
	font-weight: 700;
	letter-spacing: 0.07em;
	text-transform: uppercase;
	color: rgba(154, 163, 181, 0.85);
}

/* Selected destination: bobbing gold "you are here" marker over the ring +
   lit gold treatment. The span's ✦ text is replaced by a ▼ marker glyph. */
.tpq-map-node-pin {
	display: none;
	position: absolute;
	top: -0.95rem;
	left: 50%;
	transform: translateX(-50%);
	font-size: 0;
	pointer-events: none;
}
.tpq-map-node-pin::before {
	content: "▼";
	font-size: 0.95rem;
	color: var(--gold-bright, #ffd75e);
	text-shadow: 0 0 10px rgba(241, 196, 15, 0.9), 0 2px 4px rgba(0, 0, 0, 0.8);
}
.tpq-map-node.vv-hub-park--selected .tpq-map-node-pin {
	display: block;
	animation: tpq-map-pin-bob 1.6s ease-in-out infinite;
}
@keyframes tpq-map-pin-bob {
	0%, 100% { transform: translateX(-50%) translateY(0); }
	50% { transform: translateX(-50%) translateY(4px); }
}
.tpq-map-node.vv-hub-park--selected .tpq-map-node-ring {
	border-color: #ffe89a;
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.18),
		0 0 0 1px rgba(255, 224, 122, 0.6),
		0 0 26px -6px rgba(241, 196, 15, 0.75);
}
/* body.play-app prefix out-specifies play-polish's .vv-hub-parks-body
   .rpg-portal-cta base rule so the selected state actually goes gold. */
body.play-app .tpq-map-node.vv-hub-park--selected .tpq-map-node-name,
body.play-app .tpq-map-node.vv-hub-park--selected .tpq-map-node-cta {
	color: var(--gold-bright, #ffd75e);
}
body.play-app .tpq-map-node.vv-hub-park--selected .tpq-map-node-name {
	border-color: rgba(255, 224, 122, 0.55);
	box-shadow: var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 0 12px -4px rgba(241, 196, 15, 0.5);
}

/* ---- Single-region map (§2.1): the lone band centers and fills the stage,
   its theme becomes the whole stage's atmosphere. ---- */
.tpq-map--single .tpq-region {
	min-height: clamp(13rem, 42vw, 18rem);
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding-top: 1.1rem;
	padding-bottom: 1.4rem;
}
.tpq-map--single .tpq-region-art {
	opacity: 0.22;
}

/* ---- "Your resorts" — quiet engraved chip under the map. ---- */
body.play-app .vv-hub-parks .vv-hub-resorts-link {
	text-align: center;
	margin-top: 0.6rem;
}
body.play-app .vv-hub-parks .vv-hub-resorts-link a {
	display: inline-block;
	font-size: 0.66rem;
	font-weight: 700;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	color: var(--text-muted, #9aa3b5);
	text-decoration: none;
	border: 1px solid rgba(243, 234, 210, 0.18);
	border-radius: 999px;
	padding: 0.32rem 0.8rem;
	transition: color 0.15s ease, border-color 0.15s ease;
}
body.play-app .vv-hub-parks .vv-hub-resorts-link a:hover {
	color: var(--gold-bright, #ffd75e);
	border-color: rgba(255, 224, 122, 0.5);
}

/* ============================================================
   §4 Atmosphere motion — silhouette drift + dust motes.
   Decorative; fully disabled under reduced motion.
   ============================================================ */
.tpq-region-art {
	animation: tpq-region-drift 46s ease-in-out infinite alternate;
}
@keyframes tpq-region-drift {
	from { transform: translateX(-0.6%) scale(1.02); }
	to { transform: translateX(0.6%) scale(1.02); }
}
/* Dust motes: two parallax layers of soft dots drifting upward. */
.tpq-worldmap::before {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background-image:
		radial-gradient(1.5px 1.5px at 18% 78%, rgba(255, 224, 122, 0.5), transparent 100%),
		radial-gradient(1px 1px at 64% 32%, rgba(255, 255, 255, 0.4), transparent 100%),
		radial-gradient(1.5px 1.5px at 83% 64%, rgba(199, 125, 255, 0.4), transparent 100%),
		radial-gradient(1px 1px at 38% 14%, rgba(94, 200, 168, 0.4), transparent 100%),
		radial-gradient(1px 1px at 50% 92%, rgba(255, 255, 255, 0.3), transparent 100%);
	background-repeat: repeat;
	background-size: 240px 240px;
	opacity: 0.5;
	animation: tpq-map-dust 38s linear infinite;
	z-index: 1;
}
@keyframes tpq-map-dust {
	from { background-position: 0 0; }
	to { background-position: -60px -240px; }
}
@media (prefers-reduced-motion: reduce) {
	.tpq-region-art,
	.tpq-worldmap::before {
		animation: none;
	}
	.tpq-map-node.vv-hub-park--selected .tpq-map-node-pin {
		animation: none;
	}
}

/* ============================================================
   §2.2 Responsive hardening.
   ============================================================ */
@media (max-width: 430px) {
	.tpq-map-node {
		width: clamp(5.6rem, 27vw, 7rem);
	}
	/* Route segments get tight on phones — drop them before they crowd taps. */
	.tpq-map-route {
		display: none;
	}
	.tpq-region {
		padding-left: 0.45rem;
		padding-right: 0.45rem;
	}
}
@media (min-width: 1200px) {
	.tpq-worldmap {
		padding: 0.8rem 0 1.2rem;
	}
	.tpq-region {
		padding: 1rem 1.4rem 1.4rem;
	}
	.tpq-map-nodes {
		column-gap: 0.4rem;
	}
}

/* ============================================================
   WORLD SELECT v2 — the rotatable carousel (play-worldselect.js)
   + front-door chrome gating ("no bars until you're in a park").
   ============================================================ */

/* Front door: while the world select is showing, the game chrome does not
   exist — no action bar, no target chip, no HUD plate, anywhere on screen.
   Unscoped on purpose (the owner saw bars leak through): play.js's router
   strips tpq-front-door on every non-hub page, so it can never linger. */
body.tpq-front-door #tpqActionBar,
body.tpq-front-door #tpqTargetFrame,
body.tpq-front-door #tpqHudChrome {
	display: none !important;
}
body.play-app.tpq-diegetic-hud.tpq-front-door .play-main {
	padding-top: 0;
}
/* Fullscreen takeover: the world select IS the screen out here. */
body.tpq-front-door .tpq-worldselect {
	position: fixed;
	inset: 0;
	z-index: 145; /* over page content, under the pause overlay */
	border-radius: 0;
	border: 0;
	min-height: 100%;
	padding-bottom: calc(1.1rem + env(safe-area-inset-bottom, 0px));
	padding-top: calc(0.65rem + env(safe-area-inset-top, 0px));
}
/* Everything web-page-ish around it goes dark on the front door. */
body.tpq-front-door .vv-hub-parks .vv-hub-section-title,
body.tpq-front-door .vv-hub-parks .vv-hub-resorts-link {
	display: none;
}

/* ---- Stage ---- */
.tpq-worldselect {
	position: relative;
	border-radius: var(--radius, 14px);
	border: 1px solid rgba(243, 234, 210, 0.16);
	/* SOLID fallback color only — behind the painted scene, for 404/neutral.
	   The art is the backdrop, NOT a 50%-opacity overlay on near-black
	   (that stacking was the "dark film"). */
	background: #0b0a12;
	box-shadow:
		var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		var(--panel-shadow, 0 22px 48px -22px rgba(0, 0, 0, 0.85));
	overflow: hidden;
	padding: 0.65rem 0.65rem 1.1rem;
	min-height: clamp(20rem, 64vw, 26rem);
	display: flex;
	flex-direction: column;
}
.tpq-worldselect::after {
	/* LOCAL readability only — a soft top fade behind the title and a feathered
	   focal pool behind the carousel band, plus a gentle edge vignette. This
	   seats the medallions as the subject WITHOUT a page-wide dark film. */
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	background:
		radial-gradient(ellipse 64% 46% at 50% 66%, rgba(8, 6, 14, 0.4), transparent 70%),
		linear-gradient(180deg, rgba(8, 6, 14, 0.45) 0%, transparent 18%);
	box-shadow: inset 0 0 70px 12px rgba(8, 6, 14, 0.32);
	border-radius: inherit;
	z-index: 3;
}

/* Sky layers — one per region theme; the focused park's region fades in. */
.tpq-ws-sky {
	position: absolute;
	inset: 0;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.45s ease;
	/* Full painted night scenes (sky baked in) — cover the stage, no tiling. */
	background-repeat: no-repeat;
	background-position: center bottom;
	background-size: cover;
	image-rendering: pixelated;
	-webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%);
	mask-image: linear-gradient(180deg, transparent 0%, #000 16%);
}
.tpq-ws-sky--wdw {
	background-image: url("/assets/worldmap/region-wdw.png?v=3");
}
.tpq-ws-sky--uni {
	background-image: url("/assets/worldmap/region-uni.png?v=4");
}
.tpq-ws-sky--neutral {
	background-color: #14110f;
}
.tpq-worldselect[data-region-theme="wdw"] .tpq-ws-sky--wdw,
.tpq-worldselect[data-region-theme="uni"] .tpq-ws-sky--uni,
.tpq-worldselect[data-region-theme="neutral"] .tpq-ws-sky--neutral {
	/* The painted scene reads at near-full vividness — the carousel becomes
	   the subject via the LOCAL focal pool + bright medallions, not a global dim. */
	opacity: 0.95;
}

/* ---- Front-door chrome row: mode toggle + menu glyph (no bars out here) ---- */
.tpq-ws-chrome {
	position: relative;
	z-index: 4;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	min-height: 2.3rem;
}
.tpq-ws-mode {
	display: flex;
	justify-content: center;
}
/* Reuse the HUD mode-toggle skin on the front door. */
.tpq-ws-mode .play-mode-bar { margin: 0; }
.tpq-ws-mode .play-mode-toggle {
	display: inline-flex;
	border-radius: 999px;
	border: 1px solid rgba(201, 162, 77, 0.5);
	background: rgba(12, 14, 20, 0.88);
	box-shadow: var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 4px 14px -8px rgba(0, 0, 0, 0.85);
	overflow: hidden;
}
.tpq-ws-mode .play-mode-opt {
	border: 0;
	background: transparent;
	color: var(--text-muted, #9aa3b5);
	font-weight: 700;
	font-size: 0.72rem;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	padding: 0.4rem 0.8rem;
	cursor: pointer;
}
.tpq-ws-mode .play-mode-opt.active {
	color: #241a02;
	background: var(--gold-grad, linear-gradient(180deg, #ffe07a, #d99e06));
}
.tpq-ws-mode .play-mode-opt[disabled] { opacity: 0.55; cursor: not-allowed; }
.tpq-ws-menu {
	position: absolute;
	right: 0;
	top: 0;
	width: 2.15rem;
	height: 2.15rem;
	border-radius: 50%;
	border: 1px solid rgba(243, 234, 210, 0.28);
	background:
		radial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.08), transparent 60%),
		rgba(16, 14, 24, 0.92);
	color: var(--text, #e8ecf4);
	font-size: 1rem;
	line-height: 1;
	cursor: pointer;
	transition: transform 0.12s ease, border-color 0.15s ease;
}
.tpq-ws-menu:hover { transform: translateY(-2px); border-color: rgba(255, 224, 122, 0.6); }

/* ---- Focused-park label plaque ---- */
.tpq-ws-label {
	position: relative;
	z-index: 4;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.18rem;
	text-align: center;
	margin-top: 0.55rem;
	min-height: 4.4rem;
}
.tpq-ws-label-region {
	font-size: 0.62rem;
	font-weight: 800;
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: rgba(241, 196, 15, 0.8);
}
.tpq-ws-label-name {
	font-family: var(--font-display, "Fredoka", sans-serif);
	font-weight: 700;
	font-size: clamp(1.15rem, 4.4vw, 1.6rem);
	color: var(--text, #e8ecf4);
	text-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
	max-width: 92%;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.tpq-ws-label-cta {
	font-size: 0.64rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: var(--gold-bright, #ffd75e);
	text-shadow: 0 0 10px rgba(241, 196, 15, 0.5);
}

/* ---- The ring ---- */
.tpq-ws-ring {
	position: relative;
	z-index: 2;
	flex: 1;
	min-height: clamp(11rem, 38vw, 15rem);
	touch-action: pan-y; /* horizontal drags spin the ring; vertical still scrolls */
}
/* Fallback (controller not mounted): a plain centered wrap — everything
   still one tap. The carousel is pure enhancement. */
.tpq-worldselect:not(.is-carousel) .tpq-ws-ring {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	align-items: flex-start;
	row-gap: 0.8rem;
}
/* Carousel mode: nodes are orbit-positioned by inline transforms. */
.tpq-worldselect.is-carousel .tpq-ws-node {
	position: absolute;
	left: 50%;
	top: 52%;
	margin: 0;
	padding: 0.2rem;
	width: auto;
	will-change: transform;
}
/* On the orbit, names/CTAs collapse into the label plaque (kept readable
   for AT via the plaque's aria-live + each button's accessible name). */
.tpq-worldselect.is-carousel .tpq-map-node-name,
.tpq-worldselect.is-carousel .tpq-map-node-cta {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
	padding: 0;
	margin: -1px;
}
.tpq-worldselect.is-carousel .tpq-ws-node .tpq-map-node-ring {
	width: clamp(5.4rem, 17vw, 7.4rem);
}
.tpq-worldselect.is-carousel .tpq-ws-node.is-front .tpq-map-node-ring {
	border-color: rgba(255, 236, 168, 1);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.25),
		0 0 0 2px rgba(255, 224, 122, 0.7),
		0 0 44px 0 rgba(241, 196, 15, 0.95),
		0 0 100px -4px rgba(241, 196, 15, 0.6),
		0 10px 22px -8px rgba(0, 0, 0, 0.9);
}
/* The FOCUSED medallion reads lit from within: a bright warm disc behind
   the crest + a much brighter crest, so the center park is unmistakable. */
.tpq-worldselect.is-carousel .tpq-ws-node.is-front .tpq-map-node-crest {
	/* A subtle warm pool so the focused crest reads "lit" — the gold ring/halo
	   below is the real focal signal, not a blown-out spotlight. */
	background:
		radial-gradient(circle at 50% 46%, rgba(255, 246, 220, 0.16), rgba(72, 58, 96, 0.7) 48%, rgba(34, 27, 48, 0.98) 80%);
}
.tpq-worldselect.is-carousel .tpq-ws-node.is-front .tpq-map-node-crest-img {
	filter:
		brightness(1.25) contrast(1.08) saturate(1.15)
		drop-shadow(0 0 4px rgba(255, 236, 168, 0.45))
		drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7));
}
.tpq-worldselect.is-carousel .tpq-ws-node .tpq-map-node-pin {
	top: -0.7rem;
}
/* Orbit floor: a soft elliptical glow the medallions ride on. */
/* The orbit PLATFORM the carousel stands on — a ~50%-opaque elliptical dais
   (not just a dashed outline). Seats the medallions on a visible surface and
   gives the focal pool somewhere to land. */
.tpq-ws-ring::before {
	content: "";
	position: absolute;
	left: 50%;
	top: 52%;
	transform: translate(-50%, -50%);
	width: min(86%, 46rem);
	height: 38%;
	border-radius: 50%;
	background:
		radial-gradient(
			ellipse 100% 100% at 50% 42%,
			rgba(26, 20, 40, 0.25),
			rgba(10, 8, 18, 0.28) 70%,
			rgba(8, 6, 14, 0.25) 100%
		);
	border: 1px solid rgba(241, 196, 15, 0.28);
	box-shadow:
		0 0 40px -8px rgba(241, 196, 15, 0.18) inset,
		0 14px 34px -16px rgba(0, 0, 0, 0.7);
	pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
	.tpq-ws-sky { transition: none; }
}

/* ============================================================
   WORLD SELECT v3 — two-stage carousel: arrows, back chip,
   resort medallions, and the front-only "you are here" pin.
   ============================================================ */

/* ‹ › rotation arrows: big accessible buttons flanking the ring. */
.tpq-ws-arrow {
	position: absolute;
	top: 58%;
	transform: translateY(-50%);
	z-index: 5;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: 50%;
	border: 1px solid rgba(243, 234, 210, 0.3);
	background:
		radial-gradient(120% 90% at 50% 0%, rgba(255, 255, 255, 0.08), transparent 60%),
		rgba(16, 14, 24, 0.9);
	box-shadow: var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 4px 14px -6px rgba(0, 0, 0, 0.85);
	color: var(--gold-bright, #ffd75e);
	font-size: 1.6rem;
	line-height: 1;
	cursor: pointer;
	transition: transform 0.12s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.tpq-ws-arrow--prev { left: calc(0.5rem + env(safe-area-inset-left, 0px)); }
.tpq-ws-arrow--next { right: calc(0.5rem + env(safe-area-inset-right, 0px)); }
.tpq-ws-arrow:hover {
	transform: translateY(-50%) scale(1.08);
	border-color: rgba(255, 224, 122, 0.7);
	box-shadow: 0 0 16px -4px rgba(241, 196, 15, 0.55);
}
.tpq-ws-arrow:active { transform: translateY(-50%) scale(0.96); }
.tpq-ws-arrow:focus-visible {
	outline: 2px solid rgba(255, 224, 122, 0.8);
	outline-offset: 2px;
}
/* Arrows only make sense once the carousel is driving. */
.tpq-worldselect:not(.is-carousel) .tpq-ws-arrow { display: none; }

/* ← Resorts back chip (park stage, multi-resort players only). */
.tpq-ws-back {
	position: absolute;
	left: calc(0.6rem + env(safe-area-inset-left, 0px));
	top: calc(0.6rem + env(safe-area-inset-top, 0px));
	z-index: 5;
	border: 1px solid rgba(243, 234, 210, 0.28);
	border-radius: 999px;
	background: rgba(16, 14, 24, 0.9);
	color: var(--text, #e8ecf4);
	font-weight: 700;
	font-size: 0.7rem;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	padding: 0.45rem 0.85rem;
	cursor: pointer;
	transition: border-color 0.15s ease, color 0.15s ease;
}
.tpq-ws-back:hover {
	border-color: rgba(255, 224, 122, 0.6);
	color: var(--gold-bright, #ffd75e);
}

/* Resort medallions reuse the park ring look, a touch larger. */
.tpq-ws-node--resort .tpq-map-node-ring {
	width: clamp(6rem, 19vw, 8.2rem);
}
.tpq-worldselect.is-carousel .tpq-ws-node--resort .tpq-map-node-name {
	/* same sr-only collapse as park names (plaque carries the label) */
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
	border: 0;
	padding: 0;
	margin: -1px;
}

/* "You are here" ▼ renders ONLY on the front node — a pin hovering over a
   background medallion reads as stale (owner bug report). */
.tpq-worldselect.is-carousel .tpq-ws-node .tpq-map-node-pin {
	display: none;
}
.tpq-worldselect.is-carousel .tpq-ws-node.is-front.vv-hub-park--selected .tpq-map-node-pin {
	display: block;
	animation: tpq-map-pin-bob 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
	.tpq-worldselect.is-carousel .tpq-ws-node.is-front.vv-hub-park--selected .tpq-map-node-pin {
		animation: none;
	}
}

/* [hidden] must beat .tpq-map-node's display:flex (stage switching). */
.tpq-ws-node[hidden] {
	display: none !important;
}

/* Mode-stage medallions ("where are you playing?") */
.tpq-ws-mode-emoji {
	font-size: clamp(1.9rem, 6.5vw, 2.5rem);
	line-height: 1;
	filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.65));
}
.tpq-ws-node--mode .tpq-map-node-ring {
	width: clamp(6rem, 19vw, 8.2rem);
}

/* Front door is THE screen: even test-only chrome stays out of it. */
body.tpq-front-door .view-as-prod-toggle {
	display: none !important;
}

/* THE owner-reported boxed-picker bug: .vv-hub-parks sets display:flex
   (play.css §13494), and author rules beat the UA's [hidden]{display:none}
   regardless of specificity — so with a park selected the picker was
   logically hidden (hidden=true) yet still painted, boxed above the hub
   with the HUD up. [hidden] is authoritative for this section, full stop.
   (shop.css 710 already patched this same hole for the shop overlay.) */
body.play-app .vv-hub-parks[hidden],
body.play-app #play-hub-parks[hidden] {
	display: none !important;
}

/* The park WELCOME hero must show on the in-park hub — play-polish §2162
   hides .vv-hub-idle there because "the destination cards below are
   self-explanatory"… those cards were the phantom always-painted picker
   (now properly hidden). Without this the selected-park hub is blank. */
body.play-app.play-inpark-hub-active .vv-hub-idle--inpark {
	display: block;
}

/* "Change park" affordance on the in-park welcome hero — the primary way
   back to the world select (the pause-menu entry stays as a backup). */
.vv-hub-idle-worldmap {
	margin-top: 0.9rem;
	border: 1px solid rgba(255, 224, 122, 0.5);
	border-radius: 999px;
	background: rgba(16, 14, 24, 0.85);
	color: var(--gold-bright, #ffd75e);
	font-weight: 700;
	font-size: 0.74rem;
	letter-spacing: 0.04em;
	padding: 0.5rem 1rem;
	cursor: pointer;
	transition: transform 0.12s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.vv-hub-idle-worldmap:hover {
	transform: translateY(-2px);
	border-color: rgba(255, 224, 122, 0.85);
	box-shadow: 0 0 16px -4px rgba(241, 196, 15, 0.55);
}
.vv-hub-idle-worldmap:active { transform: translateY(0) scale(0.97); }

/* ============================================================
   THE THEMED PARK SPACE — "selecting a park transports you INTO
   the park": a fullscreen per-park stage (the duel arena painting,
   dimmed under a sky-to-ground wash + vignette) with the HUD
   framing it and a centered diegetic welcome plate.
   ============================================================ */

/* Fullscreen stage behind everything in the hub: park art (gracefully
   absent on 404 — the gradient wash carries it) + vignette. Fixed so it
   sits flush under the HUD plate and action bar at every size. */
body.play-inpark-hub-active .play-hub--park-selected::before {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	/* Light readability wash only (was 0.42/0.18/0.94 — a near-black film over
	   the painting): a soft top fade for the welcome plate + just enough bottom
	   grounding for the action bar, mid kept clear so the arena art reads. */
	background-image:
		linear-gradient(180deg, rgba(8, 7, 13, 0.18) 0%, rgba(8, 7, 13, 0.04) 40%, rgba(8, 7, 13, 0.7) 90%),
		var(--park-stage-art, none);
	background-size: cover, cover;
	background-position: center, center 30%;
	background-repeat: no-repeat;
	image-rendering: pixelated;
}
body.play-inpark-hub-active .play-hub--park-selected::after {
	content: "";
	position: fixed;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	box-shadow: inset 0 0 120px 36px rgba(8, 6, 14, 0.4); /* gentle duel vignette */
}
/* Hub content rides above the stage. */
body.play-inpark-hub-active .play-hub--park-selected .vv-hub-inner,
body.play-inpark-hub-active .play-hub--park-selected .play-hub-inner {
	position: relative;
	z-index: 1;
}

/* Center the welcome plate in the stage when it's the only thing on screen. */
body.play-inpark-hub-active
	.play-hub--park-selected
	:is(.vv-hub-inner, .play-hub-inner):has(.vv-hub-idle--inpark) {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: calc(
		100dvh - var(--tpq-hud-h, 3.3rem) - var(--tpq-bar-h, 4.9rem) -
			var(--play-safe-bottom, 0px) - 2.5rem
	);
}

/* The welcome plate: a diegetic title card, not a web panel. */
body.play-app .vv-hub-idle--inpark {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 0.3rem;
	align-self: center;
	width: min(92%, 26rem);
	padding: 1.4rem 1.3rem 1.5rem;
	border-radius: 18px;
	border: 1px solid rgba(255, 224, 122, 0.32);
	background:
		radial-gradient(120% 90% at 50% 0%, rgba(241, 196, 15, 0.08), transparent 55%),
		rgba(10, 8, 16, 0.72);
	box-shadow:
		var(--surface-edge, inset 0 1px 0 rgba(255, 255, 255, 0.06)),
		0 22px 48px -22px rgba(0, 0, 0, 0.9);
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
}
body.play-app .vv-hub-idle--inpark .vv-hub-idle-crest {
	display: flex;
	align-items: center;
	justify-content: center;
	width: clamp(4.6rem, 16vw, 5.6rem);
	aspect-ratio: 1;
	border-radius: 50%;
	border: 2px solid rgba(255, 224, 122, 0.6);
	background:
		radial-gradient(120% 85% at 50% 0%, rgba(241, 196, 15, 0.18), transparent 62%),
		rgba(14, 12, 22, 0.94);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.14),
		0 0 22px -6px rgba(241, 196, 15, 0.6),
		0 8px 18px -8px rgba(0, 0, 0, 0.9);
	margin-bottom: 0.45rem;
}
body.play-app .vv-hub-idle--inpark .vv-hub-idle-crest img {
	width: 88%;
	height: 88%;
	object-fit: contain;
	image-rendering: auto; /* smooth downscale — see .tpq-map-node-crest-img */
	filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.65));
}
body.play-app .vv-hub-idle--inpark .vv-hub-idle-kicker {
	font-size: 0.64rem;
	font-weight: 800;
	letter-spacing: 0.24em;
	text-transform: uppercase;
	color: rgba(241, 196, 15, 0.85);
	margin: 0;
}
body.play-app .vv-hub-idle--inpark .vv-hub-idle-title {
	font-family: var(--font-display, "Fredoka", sans-serif);
	font-size: clamp(1.5rem, 6vw, 2.1rem);
	font-weight: 700;
	color: var(--text, #e8ecf4);
	text-shadow: 0 2px 12px rgba(0, 0, 0, 0.85);
	margin: 0;
	line-height: 1.1;
}
body.play-app .vv-hub-idle--inpark .vv-hub-idle-lead {
	font-size: 0.82rem;
	color: var(--text-muted, #9aa3b5);
	max-width: 24rem;
	margin: 0.25rem 0 0;
}

/* Center the welcome plate against the VIEWPORT (owner: off-center on PC —
   the hub's content column isn't viewport-centered on wide screens, but the
   stage is viewport-fixed, so the plate must be too). Pinned between the
   HUD plate and the action bar at every size. */
body.play-inpark-hub-active
	.play-hub--park-selected
	.vv-hub-hero:has(.vv-hub-idle--inpark) {
	position: fixed;
	left: 0;
	right: 0;
	top: var(--tpq-hud-h, 3.3rem);
	bottom: calc(var(--tpq-bar-h, 4.9rem) + var(--play-safe-bottom, 0px));
	display: flex;
	flex-wrap: wrap;
	/* `safe center` is the whole trick for short windows: center the stack when it FITS
	   the pinned region, but the moment (season banner + welcome plate) is taller than the
	   region, fall back to start-aligned so the overflow lands on the bottom edge (reachable
	   by scroll) instead of clipping the plate top+bottom. align-content covers the wrapped
	   banner case; align-items the single-plate (no-season) case. Pair with overflow-y:auto
	   so the pinned layer scrolls instead of swallowing the park name + Change-park button. */
	align-items: safe center;
	align-content: safe center;
	justify-content: center;
	overflow-y: auto;
	overflow-x: hidden;
	overscroll-behavior: contain;
	z-index: 1;
	margin: 0;
	pointer-events: none; /* the plate itself takes the taps */
}
body.play-inpark-hub-active
	.play-hub--park-selected
	.vv-hub-hero:has(.vv-hub-idle--inpark)
	.vv-hub-idle--inpark {
	pointer-events: auto;
}

/* When a hub panel (shop, quest log, etc.) is open, play.css hides .vv-hub-hero
   — but the fixed-plate rule above wins on specificity (its :has() bumps it
   past play.css's panel-open hide), so the welcome plate stayed pinned over the
   open panel. Re-assert the hide with equal-or-greater specificity here. */
body.play-inpark-hub-active
	.play-hub--park-selected.play-hub--panel-open
	.vv-hub-hero:has(.vv-hub-idle--inpark) {
	display: none;
}

/* Same hazard under the WORLD-SELECT FRONT DOOR: the in-park welcome hero (and
   its season marquee) renders from the in-park sessionStorage flag, which the
   "Change park" gesture clears the SELECTED park but NOT the in-park flag — so
   the season banner re-rendered right on top of the world map (owner report).
   The front door is a full-screen takeover and owns the screen alone; hide the
   hub hero outright whenever it's up. body.tpq-front-door is only ever set on
   the hub world-select, so this can't strand a hero on any other screen. */
body.tpq-front-door .vv-hub-hero {
	display: none;
}
