/* Already Here — The Game. UI identity = the show cover: Anton + Montserrat,
   navy #123958 on warm cream, glass panels over the world. */

@font-face {
  font-family: 'Anton';
  src: url('assets/Anton-Regular.woff2') format('woff2'),
       url('assets/Anton-Regular.ttf') format('truetype');
  font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('assets/Montserrat-Variable.woff2') format('woff2'),
       url('assets/Montserrat-Variable.ttf') format('truetype');
  font-weight: 100 900;
  font-display: swap;
}

:root {
  --navy: #123958;
  --cream: #fdf6e3;
  --gold: #ffe6b8;
  --seaglass: #9fd4d8;
  --glass: rgba(18, 42, 66, 0.55);
  --glass-brd: rgba(253, 246, 227, 0.18);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  overscroll-behavior: none;
  background: #0b1d2e;
}
body { position: fixed; inset: 0; width: 100%; font-family: 'Montserrat', system-ui, sans-serif; }

#scene {
  position: fixed; inset: 0; width: 100dvw; height: 100dvh; display: block;
  touch-action: none; overscroll-behavior: none;
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='5.5' fill='none' stroke='%23123958' stroke-width='3'/%3E%3Ccircle cx='8' cy='8' r='5.5' fill='none' stroke='%23fdf6e3' stroke-width='1.4'/%3E%3C/svg%3E") 8 8, crosshair;
}
#reticle {
  position: fixed; z-index: 28; left: 50%; top: 50%; width: 12px; height: 12px;
  border: 1.5px solid var(--cream); border-radius: 50%; pointer-events: none;
  box-shadow: 0 0 0 2px rgba(18, 57, 88, 0.74), 0 1px 8px rgba(6, 18, 30, 0.76);
  opacity: 0; visibility: hidden; transform: translate(-50%, -50%) scale(0.82);
  transition: opacity 0.14s ease, transform 0.14s ease, visibility 0s linear 0.14s;
}
#reticle.on { opacity: 1; visibility: visible; transform: translate(-50%, -50%); transition-delay: 0s; }

/* Shared glass recipe for every HUD panel. */
#presence, #metCounter, #prompt, .toast, #unlockBanner,
.ew-slot, .ew-center, .help-card, #muteBtn, #jumpBtn, #helpBtn, #photoBtn, #travelBtn,
#chatBtn, #chatPanel, #chatDock, #glimmerCounter,
.wr-panel, .wr-handle, .wr-balance, .wr-balance-pop, .wr-card,
#raceLobby, #raceFinish, .rl-btn, .rf-btn {
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.78), rgba(8, 24, 40, 0.68));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35);
  backdrop-filter: blur(14px) saturate(1.45);
  -webkit-backdrop-filter: blur(14px) saturate(1.45);
}

/* ---------------------------------------------------------------- title -- */
#title {
  position: fixed; inset: 0; z-index: 40;
  overflow: hidden;
  background: #cfe6ee;
  /* Zero-JS cover layout. title.js replaces these viewport-relative values
     with its exact cover-mapped pixels as soon as the module reaches us. */
  --already-y: calc(50vh - 13vw);
  --already-size: 12.266667vw;
  --already-ls: 0.005882353em;
  --already-ox: 0px;
  --here-y: calc(50vh + 0.7vw);
  --here-size: 13.2vw;
  --here-ls: 0.009090909em;
  --here-ox: 0px;
  --host-y: calc(50vh + 9.5vw);
  --host-size: 2.066667vw;
  --host-ls: 0.153846154em;
  --host-ox: 0px;
}
#coverArt { position: absolute; inset: 0; transform-origin: 50% 46%; }
.cover-bg {
  position: absolute; inset: 0;
  /* WebP hero (~270KB) with a PNG fallback for engines without image-set(). */
  background: url('assets/cover-background-clean.png') center / cover no-repeat;
  background-image: image-set(url('assets/cover-background-clean.webp') type('image/webp'),
                              url('assets/cover-background-clean.png') type('image/png'));
}
/* Additive polish: the painting breathes almost imperceptibly + a soft
   painterly vignette. Text layout untouched (separate elements). */
.cover-bg {
  will-change: transform;
  animation: cover-breathe 46s ease-in-out infinite alternate;
}
@keyframes cover-breathe { from { transform: scale(1); } to { transform: scale(1.018); } }
#coverArt::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(ellipse 125% 95% at 50% 40%, transparent 60%, rgba(18, 42, 66, 0.14) 100%);
}
.cover-text {
  position: absolute; left: 50%; white-space: nowrap;
  color: var(--navy); user-select: none; opacity: 1;
}
.cover-already {
  top: var(--already-y); font-family: 'Anton', Impact, 'Arial Narrow', sans-serif; font-size: var(--already-size);
  letter-spacing: var(--already-ls);
  transform: translate(calc(-50% + var(--already-ox)), -50%);
}
.cover-here {
  top: var(--here-y); font-family: 'Anton', Impact, 'Arial Narrow', sans-serif; font-size: var(--here-size);
  letter-spacing: var(--here-ls);
  transform: translate(calc(-50% + var(--here-ox)), -50%);
}
.cover-host {
  top: var(--host-y); font-family: 'Montserrat', Arial, system-ui, sans-serif; font-weight: 700;
  font-size: var(--host-size); letter-spacing: var(--host-ls);
  transform: translate(calc(-50% + var(--host-ox)), -50%);
}

#titleUi {
  /* Anchored below the host line via the vars title.js publishes on #title,
     so the stack clears "WITH MICHAEL S GALPERT" at any viewport. Centered
     with left/right + justify-content (no transform) so the fixed-position
     hint below stays viewport-anchored. */
  position: absolute; left: 0; right: 0;
  top: calc(var(--host-y) + var(--host-size) * 0.5 + 62px);
  display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 12px;
  opacity: 0; pointer-events: none; transition: opacity 0.65s ease; z-index: 2;
}
#titleUi.ready { opacity: 1; pointer-events: auto; }
#titleUi.leave { opacity: 0; pointer-events: none; }
#titleLinks {
  position: absolute; left: 50%; top: calc(var(--host-y) + var(--host-size) * 0.5 + 8px);
  z-index: 2; display: flex; align-items: center; justify-content: center; gap: 6px;
  transform: translateX(-50%); color: rgba(18, 57, 88, 0.75);
}
#titleLinks a {
  width: 44px; height: 44px; padding: 6px; display: grid; place-items: center;
  color: inherit; border-radius: 50%; touch-action: manipulation;
  transition: color 0.18s ease, background-color 0.18s ease;
}
#titleLinks svg { display: block; width: 30px; height: 30px; }
#titleLinks a:hover, #titleLinks a:active { color: var(--navy); background: rgba(253, 246, 227, 0.24); }
#titleLinks a:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; color: var(--navy); }
#nameInput {
  width: 240px; height: 48px; padding: 0 20px; text-align: center;
  font: 700 13px 'Montserrat'; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--navy);
  background: rgba(253, 246, 227, 0.9); border: 1px solid rgba(18, 57, 88, 0.4);
  border-radius: 999px; outline: none;
  backdrop-filter: blur(8px) saturate(1.1);
  -webkit-backdrop-filter: blur(8px) saturate(1.1);
  box-shadow: 0 12px 40px rgba(18, 57, 88, 0.28), 0 8px 24px rgba(18, 57, 88, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
/* Placeholder reads as an instruction, not as disabled text. */
#nameInput::placeholder { color: rgba(18, 57, 88, 0.78); font-weight: 700; }
#nameInput:focus {
  border-color: rgba(18, 57, 88, 0.6);
  box-shadow: 0 0 0 2px rgba(255, 230, 184, 0.6), 0 12px 40px rgba(18, 57, 88, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
#enterBtn {
  height: 48px; padding: 0 44px; cursor: pointer;
  font: 700 16px 'Montserrat'; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--cream);
  background: linear-gradient(180deg, #1a4a70, #123958 55%, #0e2e48);
  border: 1px solid rgba(255, 230, 184, 0.45); border-radius: 999px;
  box-shadow: 0 12px 40px rgba(18, 57, 88, 0.45), inset 0 1px 0 rgba(255, 230, 184, 0.35),
    inset 0 -1px 0 rgba(6, 18, 30, 0.5);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
#enterBtn:hover {
  transform: translateY(-2px);
  box-shadow: 0 18px 50px rgba(18, 57, 88, 0.55), inset 0 1px 0 rgba(255, 230, 184, 0.35);
}
#titleLoading {
  /* Occupies the first entry-control slot, then yields it. */
  position: absolute; left: 50%; top: calc(var(--host-y) + var(--host-size) * 0.5 + 85px); transform: translateX(-50%);
  width: 160px; height: 2px; background: rgba(18, 57, 88, 0.18); z-index: 2;
  overflow: hidden; border-radius: 2px; transition: opacity 0.65s ease;
}
#titleLoading span {
  display: block; height: 100%; width: 40%; background: var(--navy);
  animation: load-slide 1.2s ease-in-out infinite;
}
#titleLoading.done { opacity: 0; pointer-events: none; }
@keyframes load-slide { 0% { transform: translateX(-100%); } 100% { transform: translateX(400%); } }

/* The painting wakes up: breathe forward + dissolve. */
#title.dissolve { pointer-events: none; }
#title.dissolve #coverArt {
  animation: cover-wake 2.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
#title.dissolve { animation: title-fade 2.4s cubic-bezier(0.55, 0, 0.45, 1) forwards; }
@keyframes cover-wake { to { transform: scale(1.12); } }
@keyframes title-fade {
  0% { opacity: 1; } 35% { opacity: 1; } 100% { opacity: 0; }
}

/* ------------------------------------------------------------------ hud -- */
/* HUD ZONE CONTRACT
   The announcement queue owns one LEFT-side slot shared by location cards,
   unlock banners, and toasts. Touch/coarse-pointer zones are deliberately
   non-overlapping in both orientations:
   - PORTRAIT: top-left #presence + #inviteBtn; top-right #metCounter with
     #travelBtn (68) then #glimmerCounter (126) then the Wardrobe handle
     .wr-handle (182 — the next 56px slot below the glimmer counter) then the
     Map button #mapBtn (238 — the next 56px slot below the handle) directly
     below; left rail #helpBtn, #muteBtn, then #chatBtn; announcement
     slot below that rail; prompt above bottom-centre #emoteHintBar;
     bottom-right #jumpBtn / #photoBtn column.
   - LANDSCAPE PHONE: the same top row and rails; the announcement slot sits
     beside the left rail, above the virtual stick; #mapBtn is the 238 slot in
     the bottom-right column (photo 70 → jump 126 → .wr-handle 182 → #mapBtn 238).
   No ordinary HUD element may enter the announcement slot. */
#hud {
  --announce-left: calc(24px + env(safe-area-inset-left, 0px));
  --announce-top: calc(92px + env(safe-area-inset-top, 0px));
  --announce-width: min(360px, calc(100vw - 48px));
  position: fixed; inset: 0; z-index: 20; pointer-events: none;
}
#hud[hidden] { display: none; }
#hud button { pointer-events: auto; }

/* -- presence pill (top-left): live dot + avatar cluster + label ---------- */
#presence {
  position: absolute;
  top: calc(22px + env(safe-area-inset-top, 0px)); left: calc(24px + env(safe-area-inset-left, 0px));
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px 12px 14px; border-radius: 999px;
  color: var(--cream); font: 600 11px 'Montserrat'; letter-spacing: 0.14em;
  text-transform: uppercase;
}
#presenceDot {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: radial-gradient(circle at 32% 28%, #d9ffe9 0%, #7ef0b2 45%, #3fbf7f 100%);
  border: 1px solid rgba(253, 246, 227, 0.75);
  box-shadow: 0 0 0 0 rgba(126, 240, 178, 0.5), 0 0 10px #7ef0b2;
  animation: ping 2.6s ease-out infinite;
}
/* Connecting: warm amber, a quicker pulse while relays are being reached. */
#presenceDot.connecting {
  background: radial-gradient(circle at 32% 28%, #fff2d4 0%, #ffd47a 45%, #e0a63c 100%);
  box-shadow: 0 0 0 0 rgba(255, 212, 122, 0.5), 0 0 10px #ffd47a;
  animation: ping-amber 1.1s ease-out infinite;
}
@keyframes ping-amber {
  0%   { box-shadow: 0 0 0 0 rgba(255, 212, 122, 0.5), 0 0 10px #ffd47a; }
  70%  { box-shadow: 0 0 0 6px rgba(255, 212, 122, 0), 0 0 10px #ffd47a; }
  100% { box-shadow: 0 0 0 0 rgba(255, 212, 122, 0), 0 0 10px #ffd47a; }
}
/* Tabs-only: dim and still — no relay reach, presence is this browser alone. */
#presenceDot.tabs-only {
  background: radial-gradient(circle at 32% 28%, #cfd6dc 0%, #97a3ad 55%, #5f6b74 100%);
  border-color: rgba(253, 246, 227, 0.4);
  box-shadow: none;
  animation: none;
}
@keyframes ping {
  0%   { box-shadow: 0 0 0 0 rgba(126, 240, 178, 0.45), 0 0 10px #7ef0b2; }
  70%  { box-shadow: 0 0 0 7px rgba(126, 240, 178, 0), 0 0 10px #7ef0b2; }
  100% { box-shadow: 0 0 0 0 rgba(126, 240, 178, 0), 0 0 10px #7ef0b2; }
}
#presenceCluster { display: flex; align-items: center; }
#presenceCluster:empty { display: none; }
#presenceCluster b {
  width: 13px; height: 13px; border-radius: 50%; flex: none;
  border: 2px solid rgba(253, 246, 227, 0.9);
  margin-left: -5px;
  box-shadow: 0 1px 4px rgba(6, 18, 30, 0.45);
  animation: dot-pop 0.5s cubic-bezier(0.2, 1.5, 0.3, 1) both;
}
#presenceCluster b:first-child { margin-left: 0; }
#presenceCluster u {
  text-decoration: none; margin-left: 5px;
  font: 700 9px 'Montserrat'; letter-spacing: 0.05em; opacity: 0.8;
}
@keyframes dot-pop { from { transform: scale(0); } }
@keyframes pulse { 50% { opacity: 0.5; } }

/* -- met pill (top-right): count + micro progress to next unlock ---------- */
#metCounter {
  position: absolute;
  top: calc(22px + env(safe-area-inset-top, 0px)); right: calc(24px + env(safe-area-inset-right, 0px));
  display: flex; flex-direction: row; align-items: center; gap: 12px;
  padding: 12px 18px; border-radius: 999px;
  color: var(--cream); font: 600 11px 'Montserrat'; letter-spacing: 0.12em;
  text-transform: uppercase;
}
#metCounter.tick { animation: met-tick 0.55s ease; }
@keyframes met-tick { 30% { transform: scale(1.05); } }
#metLabel { white-space: nowrap; }
#metLabel .dim { font-size: 10px; opacity: 0.62; letter-spacing: 0.16em; }
#metBar {
  display: block; width: 52px; flex: none; height: 3px; border-radius: 2px;
  background: rgba(253, 246, 227, 0.18); overflow: hidden;
}
/* Nothing met yet: no track, no gap — just the count. */
#metCounter.no-bar { gap: 0; }
#metCounter.no-bar #metBar { display: none; }
#metBar i {
  display: block; height: 100%; width: 0; min-width: 6px; border-radius: 2px;
  background: linear-gradient(90deg, var(--seaglass), var(--gold));
  box-shadow: 0 0 8px rgba(255, 230, 184, 0.55);
  transition: width 0.9s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Gathered Light stays deliberately quieter than meeting someone: one small
   balance below the social pill, with no total or progress meter to chase. */
/* Glass matches the presence/met pills exactly (shared recipe above supplies
   the gradient, border, inset highlight, drop shadow + backdrop blur); this
   block only owns slot, shape, and the letterspaced Montserrat label. */
#glimmerCounter {
  position: absolute;
  top: calc(70px + env(safe-area-inset-top, 0px));
  right: calc(24px + env(safe-area-inset-right, 0px));
  display: flex; align-items: center; gap: 7px;
  padding: 11px 16px; border-radius: 999px;
  color: var(--cream);
  font: 600 10px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  white-space: nowrap;
}
#glimmerCounter b { color: var(--gold); font-weight: 800; letter-spacing: 0.08em; }
#glimmerCounter[hidden] { display: none; }

/* -- location card: film-title entrance ----------------------------------- */
#locationCard {
  position: absolute; left: var(--announce-left); top: var(--announce-top);
  width: var(--announce-width); text-align: left; color: var(--cream);
  pointer-events: none; opacity: 0; transform: translateX(-18px);
  transition: opacity 0.55s ease, transform 0.55s ease;
}
#locationCard[hidden] { display: none; }
#locationCard.in { opacity: 1; transform: none; }
#locationCard.out { opacity: 0; transform: translateX(-12px); }
.loc-kicker {
  display: block; font: 600 10px 'Montserrat'; letter-spacing: 0.44em;
  text-transform: uppercase; color: rgba(253, 246, 227, 0.75);
  text-shadow: 0 2px 14px rgba(9, 24, 38, 0.85);
  animation: loc-kicker 0.9s ease both;
}
@keyframes loc-kicker { from { opacity: 0; transform: translateY(6px); } }
.loc-name {
  display: block; margin-top: 7px;
  font: 400 42px 'Anton'; letter-spacing: 0.1em; text-transform: uppercase;
  text-shadow: 0 4px 34px rgba(9, 24, 38, 0.8), 0 1px 0 rgba(9, 24, 38, 0.35);
  animation: loc-name 1.05s cubic-bezier(0.2, 0.9, 0.25, 1) both 0.08s;
}
@keyframes loc-name {
  from { opacity: 0; letter-spacing: 0.34em; transform: translateY(4px); filter: blur(4px); }
}
.loc-rule {
  display: block; height: 2px; width: min(320px, 100%); margin: 11px 0 0;
  background: linear-gradient(90deg, rgba(253, 246, 227, 0.9), transparent);
  animation: loc-rule 0.9s cubic-bezier(0.2, 0.8, 0.2, 1) both 0.4s;
}
@keyframes loc-rule { from { transform: scaleX(0); } }

/* -- interaction prompt with keycap --------------------------------------- */
#prompt {
  position: absolute; left: 50%; bottom: 14.5%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 13px;
  padding: 12px 21px 12px 12px; border-radius: 18px;
  color: var(--cream); font: 600 13.5px 'Montserrat'; letter-spacing: 0.03em;
}
#prompt[hidden] { display: none; }
#prompt > span { display: flex; flex-direction: column; }
.pr-loc {
  font: 700 9px 'Montserrat'; letter-spacing: 0.28em; text-transform: uppercase;
  color: rgba(253, 246, 227, 0.58); margin-top: 3px;
}
#hud.wheel-open #prompt, #hud.wheel-open #emoteHintBar { opacity: 0; transition: opacity 0.2s; }
#prompt.in { animation: prompt-in 0.38s cubic-bezier(0.2, 1.1, 0.3, 1); }
@keyframes prompt-in { from { opacity: 0; transform: translate(-50%, 12px); } }
#prompt b {
  display: grid; place-items: center; min-width: 32px; height: 32px;
  padding: 0 9px; border-radius: 9px;
  background: linear-gradient(180deg, #fffdf4, #efe6cd); color: var(--navy);
  font: 800 14px 'Montserrat';
  box-shadow: 0 3px 0 rgba(9, 24, 38, 0.5), inset 0 1px 0 #fff;
  transition: transform 0.09s ease, box-shadow 0.09s ease;
  animation: key-idle 3.2s ease-in-out infinite;
}
#prompt b.pressed {
  transform: translateY(2px);
  box-shadow: 0 1px 0 rgba(9, 24, 38, 0.5), inset 0 1px 0 #fff;
  animation: none;
}
@keyframes key-idle {
  0%, 12%, 100% { transform: translateY(0); }
  5% { transform: translateY(2px); }
}

/* -- room chat: right rail + expanded in-memory room panel ---------------- */
#chatUi { position: absolute; inset: 0; pointer-events: none; }
#chatLog {
  position: absolute; left: calc(24px + env(safe-area-inset-left, 0px)); bottom: calc(84px + env(safe-area-inset-bottom, 0px));
  width: min(360px, calc(100vw - 48px)); display: flex; flex-direction: column; gap: 5px;
  align-items: flex-start; color: var(--cream); font: 600 12px/1.35 'Montserrat'; text-shadow: 0 2px 10px rgba(6, 18, 30, 0.9); pointer-events: auto;
}
.chat-line {
  width: fit-content; max-width: 100%; padding: 5px 8px 5px 10px; border-radius: 9px;
  background: rgba(8, 24, 40, 0.53); border: 1px solid rgba(253, 246, 227, 0.10);
  box-shadow: 0 4px 15px rgba(6, 18, 30, 0.24); overflow-wrap: anywhere;
  animation: chat-line-out 0.42s ease forwards;
}
@keyframes chat-line-out { to { opacity: 0; transform: translateX(-5px); } }
.chat-name { font-weight: 800; }
.chat-notice { color: var(--gold); font-size: 11px; }
.chat-event { color: rgba(253, 246, 227, 0.62); font-size: 11px; }
.chat-line-mute, .chat-undo {
  pointer-events: auto; margin-left: 7px; border: 0; background: transparent; color: rgba(253, 246, 227, 0.7);
  font: 700 9px 'Montserrat'; letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer;
  opacity: 0; transition: opacity 0.16s ease, color 0.16s ease;
}
.chat-line:hover .chat-line-mute, .chat-notice:hover .chat-undo, .chat-line-mute:focus-visible, .chat-undo:focus-visible { opacity: 1; }
.chat-line-mute:hover, .chat-undo:hover { color: var(--gold); }
#chatPanel {
  position: absolute; z-index: 25; top: calc(76px + env(safe-area-inset-top, 0px)); left: 0;
  bottom: 0; width: 324px; padding: 12px; border-radius: 0 18px 18px 0;
  display: flex; flex-direction: column; gap: 9px; pointer-events: auto;
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.12), 0 18px 48px rgba(4, 12, 22, 0.48);
}
#chatPanel[hidden] { display: none; }
.chat-panel-head {
  display: flex; align-items: center; justify-content: space-between; padding: 2px 3px 1px;
  color: rgba(253, 246, 227, 0.66); font: 800 9px 'Montserrat'; letter-spacing: 0.26em; text-transform: uppercase;
}
.chat-panel-head b { color: var(--gold); font-size: 12px; }
#chatNoiseToggle {
  display: inline-flex; align-items: center; gap: 5px; min-width: 44px; min-height: 44px; padding: 4px 7px;
  border: 1px solid rgba(253, 246, 227, 0.18); border-radius: 9px; color: var(--cream);
  background: rgba(253, 246, 227, 0.08); cursor: pointer;
  font: 800 8px 'Montserrat'; letter-spacing: 0.12em; text-transform: uppercase;
}
#chatNoiseToggle svg { width: 15px; height: 15px; }
#chatNoiseToggle .cnt-slash { opacity: 0; transition: opacity 0.18s ease; }
#chatNoiseToggle.off { color: rgba(253, 246, 227, 0.45); }
#chatNoiseToggle.off .cnt-slash { opacity: 1; }
#chatRoster { display: flex; flex-direction: column; gap: 3px; padding-bottom: 6px; border-bottom: 1px solid rgba(253, 246, 227, 0.12); }
.chat-roster-row { min-height: 37px; display: flex; align-items: center; gap: 8px; padding: 5px 4px; border-radius: 8px; }
.chat-roster-row:hover { background: rgba(253, 246, 227, 0.06); }
.chat-roster-row.muted { opacity: 0.5; }
.chat-roster-dot { width: 8px; height: 8px; flex: none; border-radius: 50%; box-shadow: 0 0 0 1px rgba(253, 246, 227, 0.38), 0 0 8px currentColor; }
.chat-roster-copy { min-width: 0; display: flex; flex: 1; flex-direction: column; }
.chat-roster-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: 750 11px 'Montserrat'; }
.chat-roster-loc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: rgba(253, 246, 227, 0.56); font: 600 9px 'Montserrat'; letter-spacing: 0.035em; }
.chat-roster-glimmers { flex: none; color: #fff3d6; opacity: 0.66; white-space: nowrap; font: 700 9px 'Montserrat'; letter-spacing: 0.04em; }

/* -- Future Wall ---------------------------------------------------------- */
#wallUi, #wallGallery { position:fixed; inset:0; z-index:95; display:grid; place-items:center; padding:22px; background:rgba(6,18,30,.7); }
#wallUi[hidden], #wallGallery[hidden] { display:none; }
.wall-compose, #wallGallery { color:#fdf6e3; background:rgba(18,57,88,.96); border:1px solid rgba(255,243,214,.32); border-radius:18px; box-shadow:0 28px 80px rgba(0,0,0,.46); }
.wall-compose { width:min(520px,100%); padding:26px; display:grid; gap:13px; }
.wall-question { margin:0; color:#ffe6b8; font:400 24px/1.2 'Anton',Impact,sans-serif; letter-spacing:.03em; }
.wall-compose textarea { min-height:130px; resize:vertical; padding:12px; color:#123958; background:#fdf6e3; border:0; border-radius:10px; font:600 16px/1.45 'Montserrat'; }
.wall-compose button, #wallGallery button { min-height:44px; border:0; border-radius:999px; padding:0 16px; cursor:pointer; color:#123958; background:#ffe6b8; font:700 11px 'Montserrat'; letter-spacing:.07em; text-transform:uppercase; }
.wall-by { color:rgba(253,246,227,.62); }
#wallGallery { grid-template-columns:44px minmax(0,560px) 44px; gap:12px; }
#wallGallery img { max-width:100%; max-height:70vh; border-radius:12px; background:#123958; }
#wallGallery p { grid-column:2; margin:0; text-align:center; }
#wallGallery button:nth-last-child(-n+2) { grid-column:2; }
@media (orientation:portrait) and (hover:none) and (pointer:coarse) { #wallUi,#wallGallery { padding:0; } .wall-compose,#wallGallery { width:100%; height:100%; max-width:none; border-radius:0; } #wallGallery { grid-template-columns:44px minmax(0,1fr) 44px; } }
.chat-roster-row em { color: rgba(253, 246, 227, 0.58); font: 700 8px 'Montserrat'; letter-spacing: 0.09em; text-transform: uppercase; font-style: normal; }
.chat-visit { min-width: 45px; padding: 5px 7px; border-radius: 999px; cursor: pointer; color: var(--navy); background: var(--cream); border: 0; font: 800 8px 'Montserrat'; letter-spacing: 0.11em; text-transform: uppercase; }
.chat-visit:hover { background: var(--gold); transform: scale(1.04); }
#chatHistory {
  min-height: 0; flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 5px;
  padding: 2px 1px 4px; scrollbar-width: thin; pointer-events: auto;
}
#chatHistory .chat-line { width: 100%; animation: none; background: rgba(6, 18, 30, 0.34); }
#chatHistory .chat-line-mute { float: right; }
#chatDock { flex: none; padding: 8px; border-radius: 11px; pointer-events: auto; }
#chatInput {
  width: 100%; padding: 11px 12px; border: 1px solid rgba(253, 246, 227, 0.26); border-radius: 9px;
  outline: none; background: rgba(253, 246, 227, 0.96); color: var(--navy); font: 600 13px 'Montserrat';
}
#chatInput:focus { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(255, 230, 184, 0.25); }
#chatInput:disabled { opacity: 0.58; cursor: wait; }
#chatStatus { display: block; margin: 7px 3px 0; color: rgba(253, 246, 227, 0.62); font: 600 9px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase; }
#chatDock.slow #chatStatus { color: var(--gold); }
#chatUi.chat-panel-open #chatLog, #chatUi.chat-off #chatLog { display: none; }

/* -- unlock banner: NEW EMOTE --------------------------------------------- */
#unlockBanner {
  position: absolute; left: var(--announce-left); top: var(--announce-top);
  display: flex; align-items: center; gap: 15px;
  padding: 13px 28px 13px 13px; border-radius: 18px;
  border-color: rgba(255, 230, 184, 0.45);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.12), 0 14px 44px rgba(6, 18, 30, 0.45),
    0 0 34px rgba(255, 230, 184, 0.2);
  overflow: hidden; opacity: 0; pointer-events: none;
}
#unlockBanner[hidden] { display: none; }
#unlockBanner.in { opacity: 1; animation: ub-in 0.55s cubic-bezier(0.18, 1.35, 0.3, 1) forwards; }
#unlockBanner.out { animation: ub-out 0.45s ease forwards; }
@keyframes ub-in {
  from { opacity: 0; transform: translateX(-22px) scale(0.9); }
  to { opacity: 1; transform: scale(1); }
}
@keyframes ub-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateX(-16px); }
}
#unlockBanner::after {
  content: ''; position: absolute; inset: -40% -20%;
  background: linear-gradient(115deg, transparent 42%, rgba(255, 248, 226, 0.55) 50%, transparent 58%);
  transform: translateX(-130%);
}
#unlockBanner.in::after { animation: ub-shine 1.1s ease 0.3s forwards; }
@keyframes ub-shine { to { transform: translateX(130%); } }
.ub-icon {
  display: grid; place-items: center; flex: none;
  width: 47px; height: 47px; border-radius: 50%; font-size: 24px;
  background: radial-gradient(circle at 35% 30%, #fffef8, var(--gold));
  box-shadow: 0 4px 14px rgba(255, 230, 184, 0.4);
}
.ub-kicker {
  display: block; font: 700 9.5px 'Montserrat'; letter-spacing: 0.34em;
  text-transform: uppercase; color: var(--gold);
}
.ub-name {
  display: block; margin-top: 2px;
  font: 400 24px 'Anton'; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--cream);
}

/* -- toasts ---------------------------------------------------------------- */
#toasts {
  position: absolute;
  left: var(--announce-left); top: var(--announce-top); width: var(--announce-width);
  display: flex; flex-direction: column; gap: 10px; align-items: flex-start;
}
.toast {
  display: flex; align-items: center; gap: 11px;
  max-width: 330px; padding: 9px 16px 9px 9px; border-radius: 18px;
  color: var(--cream); font: 500 12.5px 'Montserrat';
  opacity: 0; transform: translateX(-24px);
  transition: opacity 0.45s cubic-bezier(0.2, 0.9, 0.3, 1),
    transform 0.45s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.toast.in { opacity: 1; transform: none; }
.toast.out { opacity: 0; transform: translateX(-16px) scale(0.96); }
.toast.bump { animation: toast-bump 0.45s ease; }
@keyframes toast-bump { 35% { transform: scale(1.06); } }
.toast-ic {
  display: grid; place-items: center; flex: none;
  width: 29px; height: 29px; border-radius: 50%; font-size: 14px;
  background: rgba(253, 246, 227, 0.12);
  box-shadow: inset 0 0 0 1px rgba(253, 246, 227, 0.1);
}
.toast b { font-weight: 700; color: #fff; }
.toast-action {
  flex: none; min-height: 30px; padding: 7px 10px; border-radius: 999px;
  color: var(--navy); background: var(--gold); text-decoration: none;
  font: 800 9px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
}
.toast-action:hover, .toast-action:focus-visible { filter: brightness(1.1); }

/* -- achievements ---------------------------------------------------------- */
#achievementsOverlay {
  position: fixed; inset: 0; z-index: 60; display: grid; place-items: center;
  padding: calc(16px + env(safe-area-inset-top, 0px)) calc(16px + env(safe-area-inset-right, 0px)) calc(16px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
  background: rgba(6, 18, 30, 0.68); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
}
#achievementsOverlay[hidden] { display: none; }
.achievements-card {
  width: min(460px, 100%); max-height: 100%; overflow: auto; border-radius: 22px;
  padding: 20px; color: var(--cream);
  background: linear-gradient(165deg, rgba(20, 46, 72, 0.94), rgba(9, 26, 42, 0.92));
  border: 1px solid rgba(253, 246, 227, 0.2); box-shadow: 0 30px 80px rgba(4, 12, 22, 0.65);
}
.achievements-card header { display: flex; align-items: center; gap: 10px; }
.achievements-card h2 { margin: 0; font: 400 25px 'Anton'; letter-spacing: 0.09em; text-transform: uppercase; }
.achievements-count { margin-right: auto; color: var(--gold); font: 800 10px 'Montserrat'; letter-spacing: 0.12em; }
.achievement-tabs { display: flex; gap: 7px; margin-top: 14px; }
.achievement-tab, .keepsake-action, .keepsake-style {
  min-height: 36px; padding: 8px 11px; border-radius: 999px; cursor: pointer;
  color: rgba(253, 246, 227, 0.74); background: rgba(253, 246, 227, 0.07);
  border: 1px solid rgba(253, 246, 227, 0.16);
  font: 800 9px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
}
.achievement-tab.active, .keepsake-action { color: var(--navy); background: var(--gold); border-color: transparent; }
.keepsake-balance { color: var(--gold); font: 800 10px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase; }
.keepsake-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-height: 54px; padding: 8px 10px; border-radius: 12px; color: rgba(253, 246, 227, 0.68); background: rgba(253, 246, 227, 0.035); border: 1px solid rgba(253, 246, 227, 0.07); }
.keepsake-row.owned { color: var(--cream); background: rgba(253, 246, 227, 0.09); border-color: rgba(255, 230, 184, 0.26); }
.keepsake-action:disabled { opacity: 0.42; cursor: default; }
.keepsake-styles { display: flex; width: 100%; gap: 6px; }
.keepsake-style { min-height: 30px; padding: 6px 9px; }
.keepsake-style.selected { color: var(--navy); background: rgba(255, 230, 184, 0.9); border-color: transparent; }
.achievements-close, .achievement-share {
  min-height: 36px; padding: 8px 12px; border-radius: 999px; cursor: pointer;
  color: rgba(253, 246, 227, 0.78); background: rgba(253, 246, 227, 0.08);
  border: 1px solid rgba(253, 246, 227, 0.18); text-decoration: none;
  font: 800 9px 'Montserrat'; letter-spacing: 0.11em; text-transform: uppercase;
}
.achievements-list { display: flex; flex-direction: column; gap: 7px; margin-top: 15px; }
.achievement-row {
  display: flex; align-items: center; gap: 10px; min-height: 54px; padding: 8px 10px;
  border-radius: 12px; color: rgba(253, 246, 227, 0.36); background: rgba(253, 246, 227, 0.035);
  border: 1px solid rgba(253, 246, 227, 0.07);
}
.achievement-row.earned { color: var(--cream); background: rgba(253, 246, 227, 0.09); border-color: rgba(255, 230, 184, 0.32); }
.achievement-copy { display: flex; flex: 1; min-width: 0; flex-direction: column; gap: 3px; }
.achievement-copy b { font: 700 12px 'Montserrat'; letter-spacing: 0.04em; }
.achievement-copy small { font: 500 10px/1.35 'Montserrat'; letter-spacing: 0.02em; }
.achievement-share { display: inline-grid; place-items: center; color: var(--navy); background: var(--gold); border-color: transparent; }
.achievements-close:hover, .achievement-share:hover, .achievements-close:focus-visible, .achievement-share:focus-visible { filter: brightness(1.12); }

/* -- radial emote wheel ---------------------------------------------------- */
#emoteWheel { position: fixed; inset: 0; z-index: 24; pointer-events: none; }
#emoteWheel[hidden] { display: none; }
.ew-backdrop {
  position: absolute; inset: 0; pointer-events: auto;
  background: radial-gradient(circle at 50% 54%, rgba(6, 18, 30, 0) 90px,
    rgba(6, 18, 30, 0.4) 320px, rgba(6, 18, 30, 0.66) 75%);
  opacity: 0; transition: opacity 0.28s ease;
}
#emoteWheel.open .ew-backdrop { opacity: 1; }
.ew-dial {
  position: absolute; left: 50%; top: 54%; width: 0; height: 0;
  --r: min(178px, 30vmin);
}
.ew-slot {
  position: absolute; left: -31px; top: -31px;
  width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  color: var(--cream); pointer-events: auto;
  transform: rotate(var(--ang)) translateX(var(--r)) rotate(calc(-1 * var(--ang)));
  animation: ew-pop 0.5s cubic-bezier(0.2, 1.3, 0.3, 1) both;
  animation-delay: var(--d);
  transition: background 0.18s, border-color 0.18s, box-shadow 0.18s;
}
@keyframes ew-pop {
  0% {
    opacity: 0;
    transform: rotate(var(--ang)) translateX(calc(var(--r) * 0.5)) rotate(calc(-1 * var(--ang))) scale(0.3);
  }
  70% {
    opacity: 1;
    transform: rotate(var(--ang)) translateX(calc(var(--r) * 1.05)) rotate(calc(-1 * var(--ang))) scale(1.05);
  }
  100% {
    opacity: 1;
    transform: rotate(var(--ang)) translateX(var(--r)) rotate(calc(-1 * var(--ang))) scale(1);
  }
}
.ew-slot em {
  font-style: normal; font-size: 26px;
  text-shadow: 0 2px 8px rgba(6, 18, 30, 0.5);
  transition: transform 0.16s cubic-bezier(0.2, 1.2, 0.3, 1);
}
.ew-slot:hover {
  background: rgba(253, 246, 227, 0.2);
  border-color: rgba(255, 230, 184, 0.75);
  box-shadow: 0 0 0 3px rgba(255, 230, 184, 0.16), 0 10px 26px rgba(6, 18, 30, 0.4);
}
.ew-slot:hover em { transform: scale(1.26); }
.ew-slot i {
  position: absolute; top: -3px; right: -3px; font-style: normal;
  display: grid; place-items: center; width: 17px; height: 17px;
  border-radius: 50%; background: var(--cream); color: var(--navy);
  font: 800 9px 'Montserrat';
  box-shadow: 0 2px 6px rgba(6, 18, 30, 0.4);
}
.ew-slot.locked { border: 1px dashed rgba(253, 246, 227, 0.28); background: rgba(9, 24, 38, 0.5); }
.ew-slot.locked em {
  filter: grayscale(1) brightness(0) invert(0.92) sepia(0.25);
  opacity: 0.3;
}
.ew-slot.locked:hover {
  background: rgba(9, 24, 38, 0.62);
  border-color: rgba(253, 246, 227, 0.42);
  box-shadow: 0 10px 26px rgba(6, 18, 30, 0.4);
}
.ew-slot.locked:hover em { transform: none; }
.ew-slot.locked i.lk {
  top: auto; bottom: -3px; right: -3px; font-size: 8px;
  background: rgba(9, 24, 38, 0.9); color: var(--cream);
  border: 1px solid rgba(253, 246, 227, 0.3);
  filter: none;
}
.ew-slot.deny { animation: ew-deny 0.38s ease; }
@keyframes ew-deny {
  25% { transform: rotate(var(--ang)) translateX(var(--r)) rotate(calc(-1 * var(--ang))) translateX(-4px); }
  55% { transform: rotate(var(--ang)) translateX(var(--r)) rotate(calc(-1 * var(--ang))) translateX(4px); }
  80% { transform: rotate(var(--ang)) translateX(var(--r)) rotate(calc(-1 * var(--ang))) translateX(-2px); }
}
.ew-center {
  position: absolute; left: -66px; top: -66px;
  width: 132px; height: 132px; border-radius: 50%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 0 14px; text-align: center; pointer-events: none;
  animation: ew-center-in 0.4s cubic-bezier(0.2, 1.2, 0.3, 1) both;
}
@keyframes ew-center-in { from { opacity: 0; transform: scale(0.6); } }
.ew-center b {
  font: 400 16px 'Anton'; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--cream); line-height: 1.1;
}
.ew-center span {
  font: 600 8.5px 'Montserrat'; letter-spacing: 0.3em; text-transform: uppercase;
  color: rgba(253, 246, 227, 0.6); white-space: nowrap;
}
.ew-center.is-locked b { color: rgba(253, 246, 227, 0.55); }
.ew-center.is-locked span { color: var(--gold); }

/* -- hint bar + mute ------------------------------------------------------- */
#emoteHintBar {
  position: absolute;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  left: 50%; transform: translateX(-50%);
  pointer-events: auto; cursor: pointer;
  color: rgba(253, 246, 227, 0.85);
  font: 600 10px 'Montserrat'; letter-spacing: 0.24em; text-transform: uppercase;
  text-shadow: 0 1px 2px rgba(6, 18, 30, 0.9), 0 2px 14px rgba(6, 18, 30, 0.6);
  padding: 6px 16px; border-radius: 999px;
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.78), rgba(8, 24, 40, 0.68));
  border: 1px solid rgba(253, 246, 227, 0.14);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35);
  backdrop-filter: blur(14px) saturate(1.45);
  -webkit-backdrop-filter: blur(14px) saturate(1.45);
  -webkit-backdrop-filter: blur(6px);
}
#muteBtn {
  position: absolute;
  bottom: calc(80px + env(safe-area-inset-bottom, 0px));
  left: calc(24px + env(safe-area-inset-left, 0px));
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; color: var(--cream);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35),
    0 6px 18px rgba(6, 18, 30, 0.4);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
#muteBtn svg {
  width: 20px; height: 20px; display: block;
  filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6));
}
#muteBtn:hover { transform: scale(1.08); border-color: rgba(253, 246, 227, 0.5); }
#muteBtn.muted { color: rgba(253, 246, 227, 0.4); }

/* "?" help button — first stop in the left rail, above sound and chat. */
#helpBtn {
  position: absolute;
  /* Above #helpOverlay (z 26) so the button stays clickable to toggle the
     guide shut, instead of the overlay swallowing the click. */
  z-index: 27;
  bottom: calc(136px + env(safe-area-inset-bottom, 0px));
  left: calc(24px + env(safe-area-inset-left, 0px));
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; color: var(--cream);
  font: 400 23px 'Anton', Impact, sans-serif; line-height: 1;
  padding-bottom: 1px; /* optically center the Anton glyph */
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35),
    0 6px 18px rgba(6, 18, 30, 0.4);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
#helpBtn:hover { transform: scale(1.08); border-color: rgba(253, 246, 227, 0.5); }
#helpBtn:active { transform: scale(0.92); border-color: rgba(253, 246, 227, 0.5); }

/* Chat closes the left rail; its panel still sheets in from the right. */
#chatBtn {
  position: absolute; width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  place-items: center; color: var(--cream); transition: transform 0.2s ease, border-color 0.2s ease;
}
#chatBtn svg { width: 20px; height: 20px; display: block; filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6)); }
#chatBtn { display: grid; left: calc(24px + env(safe-area-inset-left, 0px)); bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }
#chatBtn:hover { transform: scale(1.08); border-color: rgba(253, 246, 227, 0.5); }
#chatBtn:active { transform: scale(0.92); }

/* On-screen jump — touch sessions only (revealed via the coarse-pointer query
   below). Same glass language as the rail, in the bottom-right travel cluster. */
#jumpBtn {
  position: absolute;
  right: calc(24px + env(safe-area-inset-right, 0px));
  bottom: calc(82px + env(safe-area-inset-bottom, 0px));
  width: 64px; height: 64px; border-radius: 50%; cursor: pointer;
  display: none; place-items: center; color: var(--cream);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35),
    0 6px 18px rgba(6, 18, 30, 0.4);
  transition: transform 0.12s ease, border-color 0.2s ease;
  touch-action: none; -webkit-tap-highlight-color: transparent;
}
#jumpBtn svg {
  width: 26px; height: 26px; display: block; fill: currentColor;
  filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6));
}
#jumpBtn:active { transform: scale(0.92); border-color: rgba(253, 246, 227, 0.5); }

/* -- virtual stick: glass ring + thumb dot at the touch-down point --------
   Display-only (pointer-events none via #hud). Positioned by transform from
   input.js; hidden entirely unless a coarse-pointer session actually touches
   the left half — desktop never renders it. */
#stick {
  position: absolute; left: 0; top: 0; width: 128px; height: 128px;
  margin: -64px 0 0 -64px; border-radius: 50%;
  display: none; pointer-events: none;
  opacity: 0; transition: opacity 0.18s ease;
  /* A light outline, not a frosted disc: during rides the camera parks the
     avatar+vehicle under the left thumb, and a backdrop-blur here smeared the
     player's own character. No backdrop-filter — a faint fill + cream rim reads
     as a ring without blurring world content behind it. */
  background: radial-gradient(circle,
    rgba(20, 44, 68, 0.05) 55%, rgba(20, 44, 68, 0.16) 82%, rgba(8, 24, 40, 0.24) 100%);
  border: 1.5px solid rgba(253, 246, 227, 0.42);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.16), 0 8px 26px rgba(6, 18, 30, 0.28);
}
#stick.on { opacity: 1; }
#stick::before {
  content: ''; position: absolute; inset: -6px; border-radius: 50%;
  opacity: 0; pointer-events: none;
  background: conic-gradient(from -90deg, rgba(255, 230, 184, 0.96) 0deg, rgba(255, 230, 184, 0.96) var(--auto-arc, 0deg), rgba(255, 230, 184, 0.1) var(--auto-arc, 0deg));
  -webkit-mask: radial-gradient(circle, transparent 65%, #000 67%);
  mask: radial-gradient(circle, transparent 65%, #000 67%);
}
#stick.charging::before { opacity: 0.9; animation: stick-auto-charge var(--auto-run-hold, 1100ms) linear forwards; }
#stick.auto::before {
  --auto-arc: 360deg; opacity: 1;
  box-shadow: 0 0 16px rgba(255, 230, 184, 0.72), 0 0 0 2px rgba(18, 57, 88, 0.76);
}
#stickThumb {
  position: absolute; left: 50%; top: 50%; width: 52px; height: 52px;
  margin: -26px 0 0 -26px; border-radius: 50%;
  background: linear-gradient(160deg, rgba(253, 246, 227, 0.92), rgba(253, 246, 227, 0.68));
  border: 1px solid rgba(18, 57, 88, 0.35);
  box-shadow: 0 4px 14px rgba(6, 18, 30, 0.42), inset 0 1px 0 rgba(255, 255, 255, 0.85);
}
#stick.run { border-color: rgba(255, 230, 184, 0.62); }
#stick.run #stickThumb {
  background: linear-gradient(160deg, #fff3d6, var(--gold));
  box-shadow: 0 4px 18px rgba(255, 230, 184, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
#stickAuto {
  position: absolute; left: 50%; top: -23px; transform: translateX(-50%);
  padding: 3px 7px; border-radius: 99px; opacity: 0;
  color: var(--navy); background: rgba(255, 230, 184, 0.95);
  font: 800 9px/1 'Montserrat', sans-serif; letter-spacing: 0.12em;
  box-shadow: 0 3px 10px rgba(6, 18, 30, 0.42); transition: opacity 0.16s ease;
}
#stick.auto #stickAuto { opacity: 1; }
@property --auto-arc { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes stick-auto-charge { from { --auto-arc: 0deg; } to { --auto-arc: 360deg; } }

@media (hover: none) and (pointer: coarse) {
  #reticle { display: none !important; }
  #jumpBtn { display: grid; }
  #stick { display: block; }
  #prompt {
    pointer-events: auto; min-height: 52px;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 84px);
  }
  #prompt b { display: none; }
  #prompt:active { transform: translateX(-50%) scale(0.97); filter: brightness(1.08); }
  /* No double-tap zoom / callout / stray text selection on game UI. */
  body { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
  #hud, #title, #cinemaLayer, #emoteWheel { -webkit-tap-highlight-color: transparent; }
  #hud button, .ew-slot, #emoteHintBar, #cinemaExit { touch-action: manipulation; }
  #nameInput { -webkit-user-select: text; user-select: text; }

  /* 44px+ tap targets, thumb-reach spacing. */
  #muteBtn { width: 48px; height: 48px; }

  /* Help "?" moves to the left column, tucked under the presence pill: the
     bottom-left is the virtual-stick zone on touch, so it can't live there.
     ≥48px tap target, safe-area aware. */
  #helpBtn {
    width: 48px; height: 48px; bottom: auto;
    top: calc(78px + env(safe-area-inset-top, 0px));
    left: calc(24px + env(safe-area-inset-left, 0px));
  }
  #helpBtn:active {
    transform: scale(0.9); filter: brightness(1.28);
    border-color: rgba(255, 230, 184, 0.65);
  }
  #muteBtn, #chatBtn { width: 48px; height: 48px; left: calc(24px + env(safe-area-inset-left, 0px)); right: auto; bottom: auto; }
  #muteBtn { top: calc(134px + env(safe-area-inset-top, 0px)); }
  #chatBtn { display: grid; top: calc(190px + env(safe-area-inset-top, 0px)); }
  #chatBtn:active { transform: scale(0.9); filter: brightness(1.28); border-color: rgba(255, 230, 184, 0.65); }
  .chat-panel-head b { margin-right: auto; }
  #chatNoiseToggle { display: inline-flex; min-width: 44px; min-height: 44px; }
  #chatNoiseToggle:active { transform: scale(0.94); filter: brightness(1.16); }
  #chatPanel { top: 0; left: 0; right: auto; bottom: 0; width: min(390px, 100vw); padding: calc(16px + env(safe-area-inset-top, 0px)) 14px calc(14px + env(safe-area-inset-bottom, 0px)); border-radius: 0 18px 18px 0; }
  #chatLog { left: calc(14px + env(safe-area-inset-left, 0px)); right: auto; width: min(350px, calc(100vw - 28px)); }
  #emoteHintBar {
    display: inline-flex; align-items: center; min-height: 44px; padding: 6px 22px;
    transition: transform 0.12s ease, filter 0.12s ease;
  }
  #cinemaExit {
    display: inline-flex; align-items: center; min-height: 44px; padding: 8px 24px;
  }

  /* Pressed feedback — glass brightens and sinks, like the desktop hovers. */
  #jumpBtn.pressed {
    transform: scale(0.88); filter: brightness(1.28);
    border-color: rgba(255, 230, 184, 0.65);
  }
  #muteBtn:active {
    transform: scale(0.9); filter: brightness(1.28);
    border-color: rgba(255, 230, 184, 0.65);
  }
  #emoteHintBar:active { transform: translateX(-50%) scale(0.94); filter: brightness(1.35); }
  #cinemaExit:active { filter: brightness(0.92); }
  .ew-slot:active {
    background: rgba(253, 246, 227, 0.24);
    border-color: rgba(255, 230, 184, 0.8);
  }
  .ew-slot:active em { transform: scale(1.22); }
}

/* -- controls help overlay ------------------------------------------------- */
#helpOverlay {
  position: absolute; inset: 0; z-index: 26;
  display: grid; place-items: center; pointer-events: auto;
  background: radial-gradient(circle at 50% 45%, rgba(6, 18, 30, 0.25), rgba(6, 18, 30, 0.66));
  opacity: 0; transition: opacity 0.35s ease;
}
#helpOverlay[hidden] { display: none; }
#helpOverlay.in { opacity: 1; }
#helpOverlay.out { opacity: 0; }
.help-card {
  width: min(430px, 88vw); padding: 30px 38px 24px; border-radius: 22px;
  text-align: center;
  background: linear-gradient(165deg, rgba(20, 46, 72, 0.78), rgba(9, 26, 42, 0.7));
  transform: translateY(12px) scale(0.97);
  transition: transform 0.35s cubic-bezier(0.2, 1.1, 0.3, 1);
  /* Never taller than the viewport — the 8-row touch guide clips in short
     landscape without this; scroll the overflow rather than hide rows. */
  max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 24px);
  overflow-y: auto;
}
#helpOverlay.in .help-card { transform: none; }
.help-kicker {
  font: 700 9.5px 'Montserrat'; letter-spacing: 0.46em; text-transform: uppercase;
  color: var(--gold);
}
.help-title {
  margin-top: 7px; font: 400 30px 'Anton'; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--cream);
}
.help-title::after {
  content: ''; display: block; height: 2px; width: 64px; margin: 13px auto 3px;
  background: linear-gradient(90deg, transparent, rgba(253, 246, 227, 0.7), transparent);
}
.help-rows { margin-top: 13px; display: flex; flex-direction: column; gap: 9px; text-align: left; }
.help-row { display: flex; align-items: center; gap: 14px; }
.help-keys { display: flex; gap: 4px; min-width: 122px; justify-content: flex-end; flex: none; }
.help-keys b {
  display: grid; place-items: center; min-width: 24px; height: 24px; padding: 0 7px;
  border-radius: 6px; background: linear-gradient(180deg, #fffdf4, #efe6cd);
  color: var(--navy); font: 800 11px 'Montserrat';
  box-shadow: 0 2px 0 rgba(9, 24, 38, 0.45);
}
.help-what { color: rgba(253, 246, 227, 0.85); font: 500 12.5px 'Montserrat'; letter-spacing: 0.04em; }
.help-listen {
  display: flex; align-items: center; justify-content: center; gap: 9px; margin-top: 10px;
  padding-top: 8px; border-top: 1px solid rgba(253, 246, 227, 0.11);
}
.help-listen > span {
  flex: none; color: rgba(253, 246, 227, 0.5); font: 800 8px 'Montserrat';
  letter-spacing: 0.16em; text-transform: uppercase;
}
.help-listen-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 5px; }
.help-listen-link, .help-feedback {
  display: inline-flex; align-items: center; justify-content: center; border: 1px solid rgba(253, 246, 227, 0.17);
  border-radius: 999px; color: rgba(253, 246, 227, 0.66); text-decoration: none;
  font: 700 9px 'Montserrat'; letter-spacing: 0.04em;
  transition: color 0.16s ease, background 0.16s ease, border-color 0.16s ease;
}
.help-listen-link { min-height: 28px; padding: 5px 8px; }
.help-achievement-line { display: flex; justify-content: center; gap: 6px; margin-top: 5px; }
.help-achievements {
  min-height: 30px; padding: 6px 11px; border-radius: 999px; cursor: pointer;
  color: rgba(253, 246, 227, 0.75); background: rgba(253, 246, 227, 0.06);
  border: 1px solid rgba(253, 246, 227, 0.16);
  font: 700 9px 'Montserrat'; letter-spacing: 0.09em; text-transform: uppercase;
}
.help-achievements b { color: var(--gold); }
.help-achievements:hover, .help-achievements:focus-visible { color: var(--cream); border-color: rgba(255, 230, 184, 0.52); }
.help-foot {
  margin-top: 10px; font: 600 9.5px 'Montserrat'; letter-spacing: 0.3em;
  text-transform: uppercase; color: rgba(253, 246, 227, 0.55);
  animation: pulse 2.4s ease-in-out infinite;
}
.help-feedback {
  gap: 4px; min-height: 30px; margin-top: 6px; padding: 5px 12px;
  font-size: 10px; font-weight: 600;
}
.help-feedback b { color: var(--gold); font-weight: 800; }
.help-listen-link:hover, .help-listen-link:focus-visible, .help-feedback:hover, .help-feedback:focus-visible {
  color: var(--cream); background: rgba(253, 246, 227, 0.09); border-color: rgba(255, 230, 184, 0.52);
}

/* Short landscape (e.g. 844x390 phone): compress the 8-row guide so every row
   plus the footer fits without clipping. The max-height/overflow on .help-card
   is the safety net; this makes scrolling unnecessary. */
@media (orientation: landscape) and (max-height: 480px) {
  .help-card { padding: 16px 30px 13px; transform: translateY(8px) scale(0.98); }
  .help-title { margin-top: 4px; font-size: 22px; }
  .help-title::after { margin: 8px auto 2px; }
  .help-rows { margin-top: 8px; gap: 5px; }
  .help-row { gap: 10px; }
  .help-keys b { min-width: 22px; height: 22px; }
  .help-what { font-size: 11.5px; }
  .help-listen { margin-top: 9px; padding-top: 8px; }
  .help-feedback { margin-top: 8px; }
  .help-foot { margin-top: 10px; }
}

@media (hover: none) and (pointer: coarse) {
  .help-listen { align-items: flex-start; flex-direction: column; gap: 6px; }
  .help-listen > span { margin-left: 2px; }
  .help-listen-links { justify-content: flex-start; width: 100%; }
  .help-listen-link, .help-feedback, .help-achievements { min-height: 44px; padding: 10px 14px; touch-action: manipulation; }
  .achievements-close, .achievement-share, .achievement-tab, .keepsake-action, .keepsake-style { min-width: 44px; min-height: 44px; }
}

/* --------------------------------------------------------------- cinema -- */
#cinemaLayer { position: fixed; inset: 0; z-index: 30; }
#cinemaLayer[hidden] { display: none; }
#cinemaSurface { position: absolute; inset: 0; }
#cinemaSurface iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
#cinemaBar {
  position: absolute;
  left: 50%; bottom: calc(26px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 18px;
  padding: 12px 22px; border-radius: 14px;
  background: var(--glass); border: 1px solid var(--glass-brd);
  backdrop-filter: blur(14px); color: var(--cream);
}
#cinemaTitle { font: 700 14px 'Montserrat'; letter-spacing: 0.05em; }
#cinemaExit {
  padding: 8px 18px; cursor: pointer; border-radius: 999px;
  background: var(--cream); color: var(--navy); border: 0;
  font: 700 12px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
}

/* ------------------------------------------------------------- portrait -- */
@media (orientation: portrait) {
  /* Portrait is width-scaled just like title.js: its lockup stays in the
     upper cover while the lower third remains open for the entry stack. */
  #title {
    --already-y: calc(6vh + min(37vh, 59.677419vw));
    --already-size: min(12.266667vh, 19.784946vw);
    --already-ox: 0px;
    --here-y: calc(6vh + min(50.7vh, 81.774194vw));
    --here-size: min(13.2vh, 21.290323vw);
    --host-y: calc(6vh + min(59.5vh, 95.967742vw));
    --host-size: min(2.066667vh, 3.333333vw);
  }
  /* Opening controls become a lower-third, thumb-reachable stack. */
  #titleUi {
    top: auto; bottom: calc(15dvh + env(safe-area-inset-bottom, 0px));
    left: 24px; right: 24px;
    flex-direction: column; gap: 12px;
  }
  #nameInput, #enterBtn { width: 100%; max-width: 420px; }
  #titleLoading {
    top: auto;
    bottom: calc(15dvh + env(safe-area-inset-bottom, 0px) + 54px);
  }
  /* Keep the primary HUD clear of the notch and home indicator. */
  #presence {
    top: calc(env(safe-area-inset-top, 0px) + 14px);
    left: calc(env(safe-area-inset-left, 0px) + 14px);
    padding: 11px 14px 11px 12px; gap: 8px;
  }
  #metCounter {
    top: calc(env(safe-area-inset-top, 0px) + 14px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
    padding: 11px 14px;
  }
  #glimmerCounter {
    top: calc(env(safe-area-inset-top, 0px) + 62px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
  }
  #emoteHintBar { bottom: calc(env(safe-area-inset-bottom, 0px) + 18px); }
  #helpBtn, #muteBtn, #chatBtn { bottom: auto; }
  #helpBtn { top: calc(env(safe-area-inset-top, 0px) + 76px); }
  #muteBtn { top: calc(env(safe-area-inset-top, 0px) + 132px); }
  #chatBtn { top: calc(env(safe-area-inset-top, 0px) + 188px); }
  #chatPanel {
    top: 0; width: 100vw; border-radius: 0;
    padding: calc(16px + env(safe-area-inset-top, 0px)) 14px calc(14px + env(safe-area-inset-bottom, 0px));
  }
  .ew-dial { --r: min(38vw, 34svh); }

  /* Cinema dock reads as a finished lower-third caption, not a squeezed text
     block. cinema.js owns the bar's centering (translate(-50%)), so give the
     title real width and clamp it to two lines beside the LEAVE pill instead
     of stacking 'Sunrise Plaza — Episode 1' into four cramped lines. */
  #cinemaBar { gap: 14px; }
  #cinemaTitle {
    flex: none; width: 200px; font-size: 13px; line-height: 1.28;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }
  #cinemaExit { flex: none; }

  /* First-run auto-help eases in over the landing money shot: the backdrop
     fades slowly and lighter, and the card docks as a lower-sheet so the hero
     key-art frame (pf-blend camera, FOV 68) composes for a beat and stays
     legible above the guide. Manual opens keep the normal instant reveal. */
  #helpOverlay.auto {
    background: radial-gradient(circle at 50% 38%, rgba(6, 18, 30, 0.12), rgba(6, 18, 30, 0.5));
    transition: opacity 1.1s ease;
    place-items: end center;
  }
  #helpOverlay.auto .help-card {
    margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 12dvh);
    background: linear-gradient(165deg, rgba(20, 46, 72, 0.72), rgba(9, 26, 42, 0.66));
    transform: translateY(22px) scale(0.98);
    transition: transform 0.55s cubic-bezier(0.2, 1.1, 0.3, 1);
  }
  #helpOverlay.auto.in .help-card { transform: none; }
}

/* Keep focused text inputs in view without letting Safari move the world. */
html.kb-open #titleUi { transform: translateY(calc(var(--kb-offset, 0px) * -1)); }
html.kb-open #chatPanel { padding-bottom: calc(82px + var(--kb-offset, 0px)); }
html.kb-open #chatDock {
  position: fixed; left: 14px; right: 14px;
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--kb-offset, 0px));
  z-index: 26;
}

/* ---------------------------------------------- deterministic shot mode -- */
/* Freeze DOM motion so headless screenshots are stable frame to frame. */
html.det #hud, html.det #hud * { animation: none !important; transition: none !important; }
html.det .cover-bg { animation: none !important; }
/* Belt-and-suspenders: the freeze above is scoped to #hud, but the race view is
   a sibling tree. race.js already injects no DOM under det (it removes #raceHud
   and returns an inert stub), so this only ever matters if the block survives. */
html.det #raceHud, html.det #raceHud * { animation: none !important; transition: none !important; }

/* ------------------------------------------------ performance debugger -- */
/* F3 / ?debug=1. Compact glass panel, top-left. pointer-events pass through
   except on its own buttons, so it never steals game input. */
#debugHud {
  position: fixed; top: 10px; left: 10px; z-index: 60; width: 264px;
  padding: 8px 9px 9px; border-radius: 12px;
  font: 500 11px/1.35 ui-monospace, 'SF Mono', Menlo, monospace;
  color: var(--cream); pointer-events: none; user-select: none;
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.82), rgba(8, 24, 40, 0.74));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.45);
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
}
#debugHud[hidden] { display: none; }
#debugHud button { pointer-events: auto; }
#debugHud .dbg-head {
  display: flex; justify-content: space-between; align-items: baseline;
  font-weight: 800; letter-spacing: 0.12em; font-size: 10px;
  color: rgba(253, 246, 227, 0.62); margin-bottom: 5px;
}
#debugHud .dbg-fps { font-size: 14px; color: var(--gold); letter-spacing: 0; font-weight: 800; }
#debugHud .dbg-chart {
  display: block; width: 100%; height: 48px; border-radius: 6px;
  image-rendering: pixelated; border: 1px solid rgba(253, 246, 227, 0.1);
}
#debugHud .dbg-readout { margin: 4px 0 6px; color: rgba(253, 246, 227, 0.8); font-variant-numeric: tabular-nums; }
#debugHud .dbg-status { display: grid; grid-template-columns: 1fr 1fr; gap: 1px 10px; }
#debugHud .dbg-status > div { display: flex; justify-content: space-between; gap: 6px; }
#debugHud .dbg-status span { color: rgba(253, 246, 227, 0.5); }
#debugHud .dbg-status b { font-weight: 600; font-variant-numeric: tabular-nums; }
#debugHud .dbg-status em { color: var(--seaglass); font-style: normal; }
#debugHud .dbg-sect {
  margin: 8px 0 3px; font-size: 9px; letter-spacing: 0.14em; font-weight: 800;
  color: rgba(253, 246, 227, 0.5); border-top: 1px solid rgba(253, 246, 227, 0.1); padding-top: 6px;
}
#debugHud .dbg-timings { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
#debugHud .dbg-timings td { padding: 1px 0; }
#debugHud .dbg-timings td:nth-child(1) { color: rgba(253, 246, 227, 0.82); }
#debugHud .dbg-timings td:nth-child(2) { text-align: right; width: 40px; padding-right: 6px; }
#debugHud .dbg-timings td:nth-child(3) { width: 68px; }
#debugHud .dbg-timings i { display: block; height: 5px; border-radius: 3px; background: linear-gradient(90deg, var(--seaglass), var(--gold)); }
#debugHud .dbg-spikes { max-height: 168px; overflow: hidden; }
#debugHud .dbg-spike { padding: 2px 0; border-bottom: 1px solid rgba(253, 246, 227, 0.07); }
#debugHud .dbg-sp-top { display: flex; align-items: baseline; gap: 6px; }
#debugHud .dbg-spike b { color: #ff8f6b; font-weight: 800; min-width: 42px; }
#debugHud .dbg-spike span { flex: 1; color: rgba(253, 246, 227, 0.9); }
#debugHud .dbg-spike em { display: block; color: rgba(253, 246, 227, 0.45); font-style: normal; font-size: 9px; }
#debugHud .dbg-none { color: rgba(253, 246, 227, 0.4); padding: 2px 0; }
#debugHud .dbg-controls { display: flex; gap: 4px; margin-top: 8px; flex-wrap: wrap; align-items: center; }
#debugHud .dbg-tiers { display: inline-flex; gap: 2px; }
#debugHud button {
  font: 700 10px ui-monospace, monospace; color: var(--cream); cursor: pointer;
  background: rgba(253, 246, 227, 0.08); border: 1px solid rgba(253, 246, 227, 0.18);
  border-radius: 6px; padding: 3px 6px;
}
#debugHud button:hover { background: rgba(253, 246, 227, 0.16); }
#debugHud button.on { background: var(--gold); color: var(--navy); border-color: var(--gold); }

/* ----------------------------------------------------- travel dissolve -- */
/* Wayne's-World dreamy dissolve for T-teleport between plazas. A blurred
   cream/sea-glass veil sweeps in, fully obscures the frame while the player
   is warped, then sweeps back out — driven by the .on class from travel.js
   (opacity + backdrop-blur transitions). The .tf-ripple layer adds drifting
   radial-gradient ripples that scale and rotate-oscillate for the shimmer.
   Off-screen it is display:none, so the ripple animation costs nothing when
   idle. Sits above the HUD (z 45), below the title (z 40 is hidden in play). */
#travelFade {
  position: fixed; inset: 0; z-index: 45; pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse 120% 100% at 50% 44%,
    rgba(230, 240, 236, 0.86), rgba(184, 214, 224, 0.9) 58%, rgba(140, 180, 205, 0.95) 100%);
  backdrop-filter: blur(0px) saturate(1);
  -webkit-backdrop-filter: blur(0px) saturate(1);
  transition: opacity 0.5s ease,
    backdrop-filter 0.5s ease, -webkit-backdrop-filter 0.5s ease;
  will-change: opacity, backdrop-filter;
}
#travelFade[hidden] { display: none; }
#travelFade.on {
  opacity: 1;
  backdrop-filter: blur(26px) saturate(1.7);
  -webkit-backdrop-filter: blur(26px) saturate(1.7);
}
.tf-ripple {
  position: absolute; inset: -25%;
  mix-blend-mode: screen; filter: blur(32px);
  background:
    radial-gradient(closest-side, rgba(255, 243, 214, 0.6), transparent 72%) 20% 30% / 56% 56% no-repeat,
    radial-gradient(closest-side, rgba(159, 212, 216, 0.55), transparent 72%) 78% 62% / 60% 60% no-repeat,
    radial-gradient(closest-side, rgba(63, 143, 210, 0.42), transparent 72%) 48% 84% / 66% 66% no-repeat,
    radial-gradient(closest-side, rgba(253, 246, 227, 0.55), transparent 74%) 64% 18% / 52% 52% no-repeat;
  animation: tf-drift 3.4s ease-in-out infinite alternate;
  will-change: transform, background-position;
}
@keyframes tf-drift {
  from {
    transform: scale(1) rotate(-2.2deg);
    background-position: 20% 30%, 78% 62%, 48% 84%, 64% 18%;
  }
  to {
    transform: scale(1.16) rotate(2.2deg);
    background-position: 30% 22%, 68% 70%, 58% 76%, 54% 26%;
  }
}
@media (prefers-reduced-motion: reduce) {
  .tf-ripple { animation: none; }
}

/* ============================================================ photo mode == */
/* Camera button — sits beside the jump control in the bottom-right cluster.
   Same glass language as its neighbours; the shared recipe above supplies
   the fill. */
#photoBtn {
  position: absolute;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  right: calc(80px + env(safe-area-inset-right, 0px));
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; color: var(--cream);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35),
    0 6px 18px rgba(6, 18, 30, 0.4);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
#photoBtn svg { width: 21px; height: 21px; display: block;
  filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6)); }
#photoBtn:hover { transform: scale(1.08); border-color: rgba(253, 246, 227, 0.5); }
#photoBtn:active { transform: scale(0.92); border-color: rgba(253, 246, 227, 0.5); }

/* Travel is a touch-only compass. Its coarse-pointer placement is rehomed to
   the top-right met column by the HUD zone contract below. */
#travelBtn {
  position: absolute;
  bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  right: calc(136px + env(safe-area-inset-right, 0px));
  width: 44px; height: 44px; border-radius: 50%; cursor: pointer;
  display: none; place-items: center; color: var(--cream);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35),
    0 6px 18px rgba(6, 18, 30, 0.4);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
#travelBtn svg { width: 21px; height: 21px; display: block;
  filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6)); }
#travelBtn:hover { transform: scale(1.08); border-color: rgba(253, 246, 227, 0.5); }
#travelBtn:active { transform: scale(0.92); border-color: rgba(253, 246, 227, 0.5); }

/* Full-screen layer: shutter flash + centred glass polaroid card. */
#photoUi {
  position: fixed; inset: 0; z-index: 45;
  display: grid; place-items: center;
  background: rgba(6, 18, 30, 0);
  transition: background 0.3s ease;
  pointer-events: none;
}
#photoUi[hidden] { display: none; }
#photoUi.open {
  background: radial-gradient(circle at 50% 42%, rgba(6, 18, 30, 0.42), rgba(6, 18, 30, 0.72));
  pointer-events: auto;
}

/* Shutter flash — a quick white bloom that fades. */
#photoFlash {
  position: absolute; inset: 0; background: #fff; opacity: 0;
  pointer-events: none; mix-blend-mode: screen;
}
#photoFlash.go { animation: pc-flash 0.42s ease-out; }
@keyframes pc-flash {
  0% { opacity: 0; }
  8% { opacity: 0.92; }
  100% { opacity: 0; }
}

/* The polaroid card — slides + scales in when a shot is ready. */
#photoCard {
  width: min(460px, 92vw);
  padding: 14px 14px 16px;
  border-radius: 18px;
  background: linear-gradient(165deg, rgba(20, 46, 72, 0.86), rgba(9, 26, 42, 0.82));
  border: 1px solid rgba(253, 246, 227, 0.18);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.12), 0 30px 80px rgba(4, 12, 22, 0.6);
  backdrop-filter: blur(18px) saturate(1.4);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  transform: translateY(16px) scale(0.96);
  opacity: 0;
  transition: transform 0.36s cubic-bezier(0.2, 1.1, 0.3, 1), opacity 0.28s ease;
}
#photoUi.open #photoCard { transform: none; opacity: 1; }

/* Photo sits in a cream "print" frame — the polaroid read. */
.pc-frame {
  background: linear-gradient(180deg, #fffdf4, #f3ecda);
  padding: 8px 8px 8px; border-radius: 10px;
  box-shadow: 0 8px 22px rgba(4, 12, 22, 0.4);
  overflow: hidden; display: grid; place-items: center;
}
.pc-frame img {
  display: block; width: 100%; height: auto; max-height: 54dvh; border-radius: 4px;
  background: #0e2e48; object-fit: contain;
}

.pc-caption {
  display: flex; flex-direction: column; gap: 4px;
  margin: 12px 4px 4px; text-align: left;
}
.pc-kicker {
  font: 700 9.5px 'Montserrat', sans-serif; letter-spacing: 0.34em;
  text-transform: uppercase; color: var(--gold);
}
.pc-status {
  min-height: 16px; font: 500 12px 'Montserrat', sans-serif;
  letter-spacing: 0.02em; color: rgba(253, 246, 227, 0.72);
  word-break: break-word; transition: color 0.2s ease;
}
.pc-status.ok { color: var(--seaglass); }
.pc-status.warn { color: #ffd08a; }
.pc-status .pc-link { color: var(--cream); text-decoration: underline; text-underline-offset: 2px; }

/* Subtle upload shimmer over the status line while a POST is in flight. */
#photoUi.busy .pc-status {
  background: linear-gradient(90deg, rgba(253,246,227,0.55) 0%, rgba(255,230,184,0.95) 50%, rgba(253,246,227,0.55) 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: pc-shimmer 1.1s linear infinite;
}
@keyframes pc-shimmer { from { background-position: 120% 0; } to { background-position: -120% 0; } }

.pc-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

/* A2HS install-hint dismiss button — installHint.js injects a 32px base rule;
   raise the hit target to the 44px iOS minimum (attribute selector out-ranks
   the injected #installHint button rule regardless of load order). */
#installHint button[aria-label] { width: 44px; height: 44px; }

.pc-btn {
  flex: 1 1 auto; min-height: 44px; padding: 10px 16px; border-radius: 11px;
  cursor: pointer; white-space: nowrap;
  font: 700 11.5px 'Montserrat', sans-serif; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--cream);
  background: linear-gradient(160deg, rgba(30, 60, 90, 0.7), rgba(12, 32, 50, 0.7));
  border: 1px solid rgba(253, 246, 227, 0.18);
  transition: transform 0.15s ease, filter 0.15s ease, border-color 0.15s ease;
}
.pc-btn:hover { filter: brightness(1.12); border-color: rgba(253, 246, 227, 0.4); }
.pc-btn:active { transform: scale(0.96); }
.pc-btn:disabled { opacity: 0.55; cursor: default; transform: none; filter: none; }
.pc-primary {
  color: var(--navy);
  background: linear-gradient(180deg, #fffdf4, #ffe6b8);
  border-color: rgba(255, 230, 184, 0.6);
}
.pc-primary:hover { filter: brightness(1.05); }
.pc-ghost { flex: 0 1 auto; background: transparent; color: rgba(253, 246, 227, 0.72); }

@media (hover: none) and (pointer: coarse) {
  #photoBtn { width: 48px; height: 48px; right: calc(84px + env(safe-area-inset-right, 0px)); }
  #photoBtn:active {
    transform: scale(0.9); filter: brightness(1.28);
    border-color: rgba(255, 230, 184, 0.65);
  }
  #travelBtn { display: grid; width: 48px; height: 48px; right: calc(144px + env(safe-area-inset-right, 0px)); }
  #travelBtn:active {
    transform: scale(0.9); filter: brightness(1.28);
    border-color: rgba(255, 230, 184, 0.65);
  }
  .pc-actions { gap: 7px; }
  .pc-btn { flex: 1 1 100%; }
  .pc-ghost { flex: 1 1 100%; }
}

/* A portrait print needs its whole long frame, not the old 16:9 crop. Keep
   the card inside a phone's safe viewport while its full image stays intact. */
@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  #photoUi {
    padding: calc(12px + env(safe-area-inset-top, 0px))
      calc(12px + env(safe-area-inset-right, 0px))
      calc(12px + env(safe-area-inset-bottom, 0px))
      calc(12px + env(safe-area-inset-left, 0px));
  }
  #photoCard {
    width: min(320px, 100%); max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 24px);
    overflow: auto; overscroll-behavior: contain;
  }
  #photoCard.portrait .pc-frame img { width: auto; max-width: 100%; max-height: 39dvh; }
  #photoCard.portrait .pc-frame { min-height: 0; }
}

@media (prefers-reduced-motion: reduce) {
  #photoFlash.go { animation-duration: 0.2s; }
  #photoCard { transition: opacity 0.2s ease; transform: none; }
  #photoUi.busy .pc-status { animation: none; }
}

/* -- portrait chat sheet --------------------------------------------------- */
/* The desktop rail stays deliberately untouched. On a phone, chat is a
   bottom sheet so the horizon remains visible and thumbs stay out of the HUD. */
#chatSendBtn { display: none; }

@media (hover: none) and (pointer: coarse) {
  #chatDock { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 7px; align-items: center; }
  #chatInput { min-width: 0; font-size: 16px; }
  #chatStatus { grid-column: 1 / -1; }
  #chatSendBtn {
    display: grid; place-items: center; width: 48px; height: 48px; border: 0; border-radius: 10px;
    color: var(--navy); background: var(--gold); cursor: pointer;
    font: 800 9px 'Montserrat'; letter-spacing: 0.08em; text-transform: uppercase;
  }
  #chatSendBtn:active { transform: scale(0.92); filter: brightness(1.1); }
  #chatBtn:active { transform: scale(0.94); }

  /* Hover does not exist on a phone: leave direct actions available and wide. */
  .chat-line-mute, .chat-undo, .chat-visit {
    min-width: 44px; min-height: 44px; display: inline-grid; place-items: center;
  }
  .chat-line-mute, .chat-undo { opacity: 1; margin: 0 0 0 5px; }
  .chat-visit { padding: 0 10px; }
}

@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  /* Mid-ride, the 'Hop off' pill floated dead-center (~56% down) over the
     horizon focal point and the avatar/jetpack top. Dock it into a low band
     near #jumpBtn so it stops sitting in the visual center of the ride and the
     silhouette reads (touch portrait only; desktop/fine-pointer byte-identical). */
  #prompt { bottom: calc(env(safe-area-inset-bottom, 0px) + 116px); }

  #chatPanel {
    top: auto; left: env(safe-area-inset-left, 0px); right: env(safe-area-inset-right, 0px);
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--chat-vv-bottom, 0px));
    width: auto; height: min(55dvh, calc(100dvh - var(--chat-vv-bottom, 0px)));
    max-height: min(55dvh, calc(100dvh - var(--chat-vv-bottom, 0px)));
    padding: 12px 14px 12px; border-radius: 22px 22px 0 0;
  }
  #chatRoster {
    flex: none; flex-direction: row; overflow-x: auto; gap: 6px; padding: 2px 0 8px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  #chatRoster::-webkit-scrollbar { display: none; }
  .chat-roster-row {
    flex: 0 0 auto; min-height: 48px; max-width: 230px; gap: 7px; padding: 4px 6px;
    background: rgba(253, 246, 227, 0.06);
  }
  .chat-roster-copy { flex: 0 1 auto; }
  .chat-roster-name { font-size: 10px; }
  .chat-roster-loc { max-width: 122px; font-size: 8px; }
  .chat-roster-glimmers { font-size: 8px; }
  .chat-roster-row em { min-width: 44px; min-height: 44px; display: grid; place-items: center; }
  #chatHistory { padding-bottom: 2px; }

  /* The global keyboard pin owns canvas stability. Keep this dock inside its
     own moving sheet; the sheet's active-only visualViewport listener supplies
     --chat-vv-bottom and is removed again on close. */
  html.kb-open #chatPanel #chatDock {
    position: static; left: auto; right: auto; bottom: auto; z-index: auto;
  }

  /* Bottom-right is jump / sound / photo territory; keep quiet log lines
     above that zone and short enough that the world still reads as a world. */
  #chatLog {
    right: calc(14px + env(safe-area-inset-right, 0px));
    bottom: calc(200px + env(safe-area-inset-bottom, 0px));
    width: min(350px, calc(100vw - 28px)); max-height: 156px; overflow: hidden;
  }
}

/* ==========================================================================
   World links — invite button + "this world is full" modal (ui/worldLink.js).
   Own selectors only (#inviteBtn, #worldFull, .wf-*). Cover identity: Anton +
   Montserrat, navy glass, cream type. Injected into #hud / <body> at runtime.
   ========================================================================== */
#inviteBtn {
  position: absolute; z-index: 21;
  /* JS seats it just right of the presence pill (measured); these are the
     pre-measure fallback anchors (top-left column). */
  top: calc(22px + env(safe-area-inset-top, 0px));
  left: calc(150px + env(safe-area-inset-left, 0px));
  min-height: 44px; height: 44px; box-sizing: border-box; padding: 0 16px 0 13px;
  border-radius: 999px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  color: var(--cream);
  /* Shares the presence pill's letterspaced type so the two read as siblings. */
  font: 700 11px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.78), rgba(8, 24, 40, 0.68));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35),
    0 6px 18px rgba(6, 18, 30, 0.4);
  backdrop-filter: blur(14px) saturate(1.45); -webkit-backdrop-filter: blur(14px) saturate(1.45);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
#inviteBtn[hidden] { display: none; }
/* Narrowest widths: JS drops the label and this collapses back to a 44px disc. */
#inviteBtn.icon-only { width: 44px; padding: 0; justify-content: center; gap: 0; }
#inviteBtn.icon-only .ib-label { display: none; }
#inviteBtn .ib-label { line-height: 1; padding-top: 1px; }
#inviteBtn svg {
  width: 18px; height: 18px; display: block; flex: none;
  filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6));
}
#inviteBtn:hover { transform: scale(1.08); border-color: rgba(253, 246, 227, 0.5); }
#inviteBtn:active { transform: scale(0.92); border-color: rgba(253, 246, 227, 0.5); }
/* Post-"new world" nudge: a couple of gold rings so the eye lands on the way
   to bring someone into the empty world just minted. */
#inviteBtn.pulse { animation: invite-pulse 1.3s ease-out 2; }
@keyframes invite-pulse {
  0%   { box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35), 0 0 0 0 rgba(255, 230, 184, 0.55); }
  70%  { box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35), 0 0 0 12px rgba(255, 230, 184, 0); }
  100% { box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35), 0 0 0 0 rgba(255, 230, 184, 0); }
}

#worldFull {
  position: fixed; inset: 0; z-index: 36;
  display: grid; place-items: center; pointer-events: auto;
  padding: calc(env(safe-area-inset-top, 0px) + 16px) 16px calc(env(safe-area-inset-bottom, 0px) + 16px);
  background: radial-gradient(circle at 50% 45%, rgba(6, 18, 30, 0.42), rgba(6, 18, 30, 0.74));
  opacity: 0; transition: opacity 0.32s ease;
}
#worldFull[hidden] { display: none; }
#worldFull.in { opacity: 1; }
#worldFull.out { opacity: 0; }
.wf-card {
  width: min(430px, 90vw); padding: 32px 34px 26px; border-radius: 22px; text-align: center;
  background: linear-gradient(165deg, rgba(20, 46, 72, 0.82), rgba(9, 26, 42, 0.74));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 24px 60px rgba(6, 18, 30, 0.5);
  backdrop-filter: blur(16px) saturate(1.4); -webkit-backdrop-filter: blur(16px) saturate(1.4);
  transform: translateY(14px) scale(0.96);
  transition: transform 0.34s cubic-bezier(0.2, 1.1, 0.3, 1);
  max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 24px);
  overflow-y: auto;
}
#worldFull.in .wf-card { transform: none; }
.wf-kicker {
  font: 700 9.5px 'Montserrat'; letter-spacing: 0.46em; text-transform: uppercase; color: var(--gold);
}
.wf-title {
  margin-top: 9px; font: 400 34px 'Anton'; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--cream); line-height: 1.02;
}
.wf-title::after {
  content: ''; display: block; height: 2px; width: 64px; margin: 15px auto 3px;
  background: linear-gradient(90deg, transparent, rgba(253, 246, 227, 0.7), transparent);
}
.wf-body {
  margin-top: 12px; color: rgba(253, 246, 227, 0.82);
  font: 500 13.5px/1.5 'Montserrat'; letter-spacing: 0.03em;
}
.wf-actions { margin-top: 22px; display: flex; flex-direction: column; gap: 10px; }
.wf-btn {
  width: 100%; min-height: 46px; padding: 12px 18px; border-radius: 12px; cursor: pointer;
  font: 700 12px 'Montserrat'; letter-spacing: 0.16em; text-transform: uppercase;
  transition: transform 0.16s ease, background 0.16s ease, border-color 0.16s ease, opacity 0.16s ease;
}
.wf-btn:disabled { opacity: 0.6; cursor: default; }
.wf-primary {
  border: 0; color: var(--navy);
  background: linear-gradient(180deg, #fffdf4, #efe6cd);
  box-shadow: 0 3px 0 rgba(9, 24, 38, 0.45);
}
.wf-primary:not(:disabled):hover { transform: translateY(-1px); }
.wf-primary:not(:disabled):active { transform: translateY(1px); box-shadow: 0 1px 0 rgba(9, 24, 38, 0.45); }
.wf-ghost {
  background: transparent; color: rgba(253, 246, 227, 0.82);
  border: 1px solid rgba(253, 246, 227, 0.28);
}
.wf-ghost:not(:disabled):hover { border-color: rgba(253, 246, 227, 0.55); color: var(--cream); }

@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  .wf-card { width: min(430px, 92vw); padding: 28px 22px 22px; }
  .wf-title { font-size: 30px; }
}
@media (prefers-reduced-motion: reduce) {
  #worldFull, #worldFull .wf-card { transition: none; }
  #inviteBtn.pulse { animation: none; }
}

/* Touch HUD zone implementation. These overrides are intentionally gated so
   desktop/fine-pointer offsets above remain unchanged. */
@media (hover: none) and (pointer: coarse) {
  #presence {
    top: calc(env(safe-area-inset-top, 0px) + 14px);
    left: calc(env(safe-area-inset-left, 0px) + 14px);
    min-height: 44px; box-sizing: border-box;
  }
  #metCounter {
    top: calc(env(safe-area-inset-top, 0px) + 14px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
    min-height: 44px; box-sizing: border-box;
  }
  /* Compass is a top-right control now, directly under the met pill. */
  #travelBtn {
    display: grid; width: 48px; height: 48px;
    top: calc(env(safe-area-inset-top, 0px) + 68px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
    bottom: auto; left: auto;
  }
  #glimmerCounter {
    top: calc(env(safe-area-inset-top, 0px) + 126px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
  }

  /* Presence owns top-left; its dynamically measured invite remains beside
     it. The left rail begins below both, clear of the virtual-stick zone. */
  #helpBtn {
    top: calc(env(safe-area-inset-top, 0px) + 70px);
    left: calc(env(safe-area-inset-left, 0px) + 14px);
  }
  #muteBtn {
    top: calc(env(safe-area-inset-top, 0px) + 126px);
    left: calc(env(safe-area-inset-left, 0px) + 14px);
    right: auto; bottom: auto;
  }
  #chatBtn {
    top: calc(env(safe-area-inset-top, 0px) + 182px);
    left: calc(env(safe-area-inset-left, 0px) + 14px);
  }

  /* One left-side announcement slot below the presence rail. The queue
     ensures only one of these owns a visible rectangle at a time. */
  #hud {
    --announce-left: calc(env(safe-area-inset-left, 0px) + 14px);
    --announce-top: calc(env(safe-area-inset-top, 0px) + 250px);
    --announce-width: min(320px, calc(100vw - 28px));
  }
  .toast { max-width: 100%; }
  #locationCard .loc-name { font-size: min(38px, 10vw); }

  /* Dedicated bottom-right action column: jump and photo. */
  #photoBtn {
    width: 48px; height: 48px;
    right: calc(env(safe-area-inset-right, 0px) + 14px);
    bottom: calc(env(safe-area-inset-bottom, 0px) + 70px);
  }
  #jumpBtn {
    right: calc(env(safe-area-inset-right, 0px) + 14px);
    bottom: calc(env(safe-area-inset-bottom, 0px) + 126px);
  }

  /* A narrow portrait has a fixed-width, glanceable met pill. Its full title
     is preserved as the element title and the emote wheel already shows total. */
  @media (max-width: 560px) {
    #metCounter {
      width: 112px; justify-content: center; gap: 0; padding: 10px 8px;
      font-size: 11px; letter-spacing: 0.08em;
    }
    #metCounter.compact #metBar { display: none; }
    #metCounter.compact #metLabel { text-align: center; }
    #metCounter.compact #metLabel .dim { font-size: 10px; letter-spacing: 0.07em; opacity: 0.82; }
  }
}

@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  /* Portrait's centred prompt is above the complete right action column, so a
     long action label cannot touch the jump target. */
  #prompt { bottom: calc(env(safe-area-inset-bottom, 0px) + 200px); }
}

@media (orientation: landscape) and (hover: none) and (pointer: coarse) {
  /* The upper-left lane is clear of both the 44px rail and the lower stick. */
  #hud {
    --announce-left: calc(env(safe-area-inset-left, 0px) + 76px);
    --announce-top: calc(env(safe-area-inset-top, 0px) + 78px);
    --announce-width: min(280px, calc(50vw - 90px));
  }
  #locationCard .loc-name { font-size: min(38px, 5vw); }
}

/* ==========================================================================
   Privacy surfaces (ui/privacy.js): the cover link + the one-time notice card.
   Own selectors only (#privacyLink, .privacy-note, .privacy-actions, .privacy-ok).
   ========================================================================== */
#privacyLink {
  position: absolute; z-index: 41;
  right: calc(env(safe-area-inset-right, 0px) + 16px);
  bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
  color: var(--navy); text-decoration: none;
  font: 700 10px 'Montserrat'; letter-spacing: 0.18em; text-transform: uppercase;
  padding: 8px 10px; border-radius: 999px; opacity: 0.72;
  text-shadow: 0 1px 6px rgba(253, 246, 227, 0.55);
  transition: opacity 0.2s ease;
}
#privacyLink:hover { opacity: 1; }

/* The notice reuses the .toast glass + the announcement slot; it just needs a
   second action, so the actions wrap onto their own justified row. */
.privacy-note { flex-wrap: wrap; max-width: 360px; }
.privacy-note .toast-copy { flex: 1 1 auto; }
.privacy-actions { display: inline-flex; gap: 8px; width: 100%; justify-content: flex-end; margin-top: 4px; }
.privacy-ok {
  flex: none; min-height: 30px; padding: 7px 13px; border-radius: 999px; cursor: pointer;
  color: var(--cream); background: transparent; border: 1px solid rgba(253, 246, 227, 0.3);
  font: 800 9px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
  transition: border-color 0.16s ease, color 0.16s ease;
}
.privacy-ok:hover, .privacy-ok:focus-visible { border-color: rgba(253, 246, 227, 0.6); }

/* ==========================================================================
   Presence diagnostics card (ui/netDiag.js) — tap the presence pill to open.
   Own selectors: #netDiag, .nd-*. Cover glass identity.
   ========================================================================== */
#netDiag {
  position: absolute; z-index: 30; pointer-events: auto;
  width: min(300px, calc(100vw - 28px)); padding: 14px 16px; border-radius: 16px;
  color: var(--cream);
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.9), rgba(8, 24, 40, 0.84));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 18px 44px rgba(6, 18, 30, 0.5);
  backdrop-filter: blur(16px) saturate(1.4); -webkit-backdrop-filter: blur(16px) saturate(1.4);
  opacity: 0; transform: translateY(-6px) scale(0.98);
  transition: opacity 0.2s ease, transform 0.2s ease;
}
#netDiag[hidden] { display: none; }
#netDiag.in { opacity: 1; transform: none; }
#netDiag.out { opacity: 0; }
.nd-head {
  display: flex; align-items: center; justify-content: space-between;
  font: 700 9.5px 'Montserrat'; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--gold); margin-bottom: 10px;
}
.nd-close {
  background: none; border: 0; color: rgba(253, 246, 227, 0.6); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 4px; margin: -4px;
}
.nd-close:hover { color: var(--cream); }
.nd-rows { display: grid; grid-template-columns: auto 1fr; gap: 6px 12px; align-items: start; }
.nd-rows dt {
  font: 700 9px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(253, 246, 227, 0.5); padding-top: 2px;
}
.nd-rows dd { font: 600 12px 'Montserrat'; word-break: break-word; }
.nd-sig { text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
.nd-online { color: #7ef0b2; }
.nd-connecting { color: #ffd47a; }
.nd-tabs { color: #97a3ad; }
.nd-relay { display: flex; align-items: center; gap: 7px; font-size: 11.5px; }
.nd-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.nd-dot.ok { background: #7ef0b2; }
.nd-dot.bad { background: #f0857e; }
.nd-dot.wait { background: #97a3ad; }
.nd-copy {
  margin-top: 12px; width: 100%; min-height: 40px; border-radius: 10px; cursor: pointer;
  border: 0; color: var(--navy); background: linear-gradient(180deg, #fffdf4, #efe6cd);
  font: 800 10px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  box-shadow: 0 2px 0 rgba(9, 24, 38, 0.4);
}
.nd-copy:active { transform: translateY(1px); box-shadow: 0 1px 0 rgba(9, 24, 38, 0.4); }

/* Blocked presence (content-blocker killing sockets): dim red dot + card note. */
#presenceDot.blocked {
  background: radial-gradient(circle at 32% 28%, #ffd9d4 0%, #f0857e 55%, #b8443c 100%);
  border-color: rgba(253, 246, 227, 0.45);
  box-shadow: 0 0 8px rgba(240, 133, 126, 0.5); animation: none;
}
.nd-sig.nd-blocked { color: #f0857e; }
.nd-blockmsg {
  margin: 0 0 10px; padding: 9px 11px; border-radius: 10px;
  background: rgba(240, 133, 126, 0.12); border: 1px solid rgba(240, 133, 126, 0.35);
  color: #ffd9d4; font: 600 11.5px/1.45 'Montserrat';
}
.nd-blockmsg b { color: #fff; }
.nd-relay em { font-style: normal; color: rgba(253, 246, 227, 0.45); font-size: 10px; }

/* ==========================================================================
   -- wardrobe -- (ui/wardrobe.js)
   The slide-in locker: a right-edge drawer on desktop, a bottom sheet in
   portrait, a narrower drawer on landscape phones. Motion is transform/opacity
   ONLY — the glass is baked in and never animates its backdrop-filter. Own
   selectors: #wardrobe, .wr-*. z-index 32 (registry: reticle 28, cinema/netDiag
   30, wardrobe 32, tapVeil 36, title 40, achievements 60). The panel never
   mounts under ?det=1 / shot, so det screenshots stay byte-stable.
   ========================================================================== */
#wardrobe { position: fixed; inset: 0; z-index: 32; pointer-events: none; }

.wr-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* scrim — outside-tap close target, fades with the panel */
.wr-scrim {
  position: absolute; inset: 0; background: rgba(6, 16, 26, 0.44);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.wr-root.open .wr-scrim { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity 0.42s ease; }
.wr-root.closing .wr-scrim { opacity: 0; visibility: visible; }

/* panel — desktop right drawer. Shared glass recipe supplies the gradient,
   border, inset highlight, shadow + backdrop blur; this owns shape + motion. */
.wr-panel {
  position: absolute; top: 0; right: 0; height: 100%;
  width: min(560px, 46vw);
  border-radius: 18px 0 0 18px;
  display: flex; flex-direction: column;
  color: var(--cream); overflow: hidden;
  pointer-events: auto; visibility: hidden;
  transform: translateX(102%);
  transition: transform 0.3s cubic-bezier(0.4, 0, 1, 1);
  will-change: transform;
}
.wr-root.open .wr-panel, .wr-root.closing .wr-panel { visibility: visible; }
/* 420ms overshoot open (~6px), 300ms accelerate-out close (base transition). */
.wr-root.open .wr-panel { transform: translateX(0); transition: transform 0.42s cubic-bezier(0.22, 1.18, 0.3, 1); }

/* grab-nub — portrait only (hidden on desktop) */
.wr-nub { display: none; }

/* header */
.wr-head {
  flex: none; display: flex; align-items: center; gap: 12px;
  padding: 18px 18px 12px;
}
.wr-title { font: 800 15px 'Anton', 'Montserrat'; letter-spacing: 0.16em; color: var(--cream); }
.wr-balance {
  margin-left: auto; padding: 8px 14px; border-radius: 999px;
  color: var(--cream); font: 700 11px 'Montserrat'; letter-spacing: 0.1em;
  cursor: pointer; white-space: nowrap;
}
.wr-balance-n { color: var(--gold); }
.wr-close {
  flex: none; width: 40px; height: 40px; border-radius: 12px;
  background: rgba(253, 246, 227, 0.06); border: 1px solid var(--glass-brd);
  color: var(--cream); font-size: 17px; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
  transition: background 0.16s ease, transform 0.16s ease;
}
.wr-close:hover { background: rgba(253, 246, 227, 0.13); }
.wr-close:active { transform: scale(0.94); }

.wr-balance-pop {
  position: absolute; top: 60px; right: 18px; z-index: 2;
  max-width: 280px; padding: 12px 14px; border-radius: 14px;
  color: var(--cream); font: 500 12px/1.5 'Montserrat';
}

/* tabs */
.wr-tabs { flex: none; display: flex; gap: 8px; padding: 4px 18px 10px; }
.wr-tab {
  flex: 1; padding: 11px 10px; border-radius: 12px;
  background: transparent; border: 1px solid transparent;
  color: rgba(253, 246, 227, 0.6); cursor: pointer;
  font: 800 12px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  transition: color 0.16s ease, background 0.16s ease, border-color 0.16s ease;
}
.wr-tab:hover { color: var(--cream); }
.wr-tab.active {
  color: var(--navy); background: linear-gradient(160deg, var(--seaglass), #cbe9ec);
  border-color: transparent; box-shadow: 0 4px 14px rgba(159, 212, 216, 0.28);
}

/* stage: preview + detail */
.wr-stage { flex: none; display: flex; gap: 14px; padding: 4px 18px 12px; }
.wr-preview {
  position: relative; flex: 0 0 44%; aspect-ratio: 3 / 4; min-height: 190px;
  border-radius: 16px; overflow: hidden;
  background: radial-gradient(120% 90% at 50% 20%, rgba(159, 212, 216, 0.16), rgba(8, 22, 36, 0.5));
  border: 1px solid var(--glass-brd);
  touch-action: pan-y;
}
/* touch-action:none so a horizontal grab-to-rotate is never stolen as a scroll;
   grab/grabbing cursor makes the drag affordance obvious on desktop. */
.wr-canvas { display: block; width: 100%; height: 100%; touch-action: none; cursor: grab; }
.wr-canvas.grabbing { cursor: grabbing; }
.wr-fallback {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: radial-gradient(circle at 50% 38%, rgba(159, 212, 216, 0.22), transparent 60%);
}
/* Only shown on WebGL-context failure — the class's display:grid otherwise wins
   over the [hidden] attribute, compositing the milky bust over a live avatar. */
.wr-fallback[hidden] { display: none; }
.wr-fallback::before {
  content: ''; width: 46%; height: 58%; border-radius: 44% 44% 40% 40% / 55% 55% 45% 45%;
  background: linear-gradient(180deg, rgba(253, 246, 227, 0.5), rgba(159, 212, 216, 0.28));
  box-shadow: inset 0 6px 18px rgba(6, 18, 30, 0.35);
}
/* Teaser silhouette shroud — matte tint (teaserColor) over the default avatar
   with a big ??? so the card stays honest about what's still out there. */
.wr-teaser-veil {
  position: absolute; inset: 0; display: grid; place-items: center;
  background: linear-gradient(160deg, var(--wr-teaser-tint, #16283b), rgba(8, 20, 34, 0.94));
  backdrop-filter: saturate(0) brightness(0.5); -webkit-backdrop-filter: saturate(0) brightness(0.5);
}
.wr-teaser-veil[hidden] { display: none; }
.wr-teaser-q { font: 800 40px 'Anton', 'Montserrat'; letter-spacing: 0.12em; color: rgba(253, 246, 227, 0.5); }

/* WEARING affordance on the hero: a seaglass chip + a ring when the previewed
   look is the one currently worn — keeps "what am I wearing" unambiguous. */
.wr-wearing-badge {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  padding: 5px 10px; border-radius: 999px;
  background: var(--seaglass); color: var(--navy);
  font: 800 9px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  box-shadow: 0 2px 8px rgba(6, 18, 30, 0.4);
}
.wr-wearing-badge[hidden] { display: none; }
.wr-preview.is-wearing { box-shadow: inset 0 0 0 2px var(--seaglass), 0 8px 22px rgba(6, 18, 30, 0.3); }

.wr-arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(8, 22, 36, 0.55); border: 1px solid var(--glass-brd);
  color: var(--cream); font-size: 18px; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  transition: background 0.16s ease;
}
.wr-arrow:hover { background: rgba(18, 44, 68, 0.8); }
.wr-arrow-prev { left: 8px; }
.wr-arrow-next { right: 8px; }

.wr-detail { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; padding-top: 6px; }
.wr-name { font: 800 22px 'Anton', 'Montserrat'; letter-spacing: 0.02em; color: var(--cream); line-height: 1.05; }
.wr-rarity {
  align-self: flex-start; padding: 4px 10px; border-radius: 999px;
  font: 800 10px 'Montserrat'; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--navy); background: var(--seaglass);
}
.wr-rarity[data-rarity="rare"] { background: var(--gold); }
.wr-rarity[data-rarity="epic"] { background: #c9b6ff; }
.wr-flavor { font: 500 13px/1.5 'Montserrat'; color: rgba(253, 246, 227, 0.74); }

/* chips */
.wr-chips { flex: none; display: flex; gap: 8px; padding: 2px 18px 10px; }
.wr-chip {
  padding: 8px 14px; border-radius: 999px;
  background: rgba(253, 246, 227, 0.05); border: 1px solid var(--glass-brd);
  color: rgba(253, 246, 227, 0.66); cursor: pointer;
  font: 700 10px 'Montserrat'; letter-spacing: 0.12em; text-transform: uppercase;
  transition: color 0.15s ease, background 0.15s ease;
}
.wr-chip:hover { color: var(--cream); }
.wr-chip.active { color: var(--navy); background: var(--cream); border-color: transparent; }

/* ribbon — free player-accent colour picker; STYLE tab only. */
.wr-ribbon { flex: none; display: flex; align-items: center; gap: 12px; padding: 0 18px 10px; }
.wr-root[data-tab="shop"] .wr-ribbon { display: none; }
.wr-ribbon-label { font: 800 10px 'Montserrat'; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(253, 246, 227, 0.6); flex: none; }
.wr-ribbon-row { display: flex; gap: 8px; flex-wrap: wrap; }
.wr-ribbon-sw {
  width: 24px; height: 24px; border-radius: 50%; cursor: pointer; padding: 0;
  background: var(--rc); border: 2px solid rgba(253, 246, 227, 0.25);
  box-shadow: 0 2px 6px rgba(6, 18, 30, 0.35);
  transition: transform 0.14s ease, border-color 0.14s ease;
}
.wr-ribbon-sw:hover { transform: scale(1.1); }
.wr-ribbon-sw.selected { border-color: var(--cream); box-shadow: 0 0 0 2px var(--cream), 0 2px 8px rgba(6, 18, 30, 0.4); }
.wr-ribbon-sw:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* shape — free body-proportion picker; STYLE tab only. FREE axis: every chip
   always enabled, no price/owned gating (unlike the SHOP grid). */
.wr-shape { flex: none; display: flex; align-items: center; gap: 12px; padding: 0 18px 10px; }
.wr-root[data-tab="shop"] .wr-shape { display: none; }
.wr-shape-label { font: 800 10px 'Montserrat'; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(253, 246, 227, 0.6); flex: none; }
.wr-shape-row { display: flex; gap: 8px; flex-wrap: wrap; }
.wr-shape-chip {
  padding: 8px 14px; border-radius: 999px;
  background: rgba(253, 246, 227, 0.05); border: 1px solid var(--glass-brd);
  color: rgba(253, 246, 227, 0.66); cursor: pointer;
  font: 700 10px 'Montserrat'; letter-spacing: 0.12em; text-transform: uppercase;
  transition: color 0.15s ease, background 0.15s ease;
}
.wr-shape-chip:hover { color: var(--cream); }
.wr-shape-chip.selected { color: var(--navy); background: var(--cream); border-color: transparent; }
.wr-shape-chip:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* grid — the sole scroll region */
.wr-grid {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: max-content; gap: 10px;
  padding: 4px 18px 14px; align-content: start;
}
.wr-grid.wr-xfade { animation: wr-xfade 140ms ease; }
@keyframes wr-xfade { from { opacity: 0.2; } to { opacity: 1; } }

/* SHOP section headers span the whole grid row (FEATURED / DAILY PICKS …). */
.wr-section {
  grid-column: 1 / -1; display: flex; align-items: baseline; gap: 10px;
  margin: 6px 0 0; padding-top: 4px;
}
.wr-section:first-child { margin-top: 0; }
.wr-section-label { font: 800 11px 'Montserrat'; letter-spacing: 0.16em; text-transform: uppercase; color: var(--cream); }
.wr-section-sub { font: 700 9.5px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(255, 214, 130, 0.85); }

.wr-card {
  position: relative; display: flex; flex-direction: column; gap: 6px;
  padding: 8px; border-radius: 14px; cursor: pointer; color: var(--cream);
  text-align: left; min-height: 44px;
  transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease;
}
.wr-root.open .wr-card {
  animation: wr-rise 0.42s cubic-bezier(0.22, 1.05, 0.3, 1) both;
  animation-delay: calc(var(--wr-i, 0) * 46ms);
}
@keyframes wr-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.wr-card:hover { transform: translateY(-2px); border-color: rgba(253, 246, 227, 0.3); }
.wr-card.selected { border-color: var(--seaglass); box-shadow: 0 0 0 1px var(--seaglass), 0 8px 22px rgba(6, 18, 30, 0.4); }
/* padding-top ratio (not aspect-ratio) so the square participates in grid row
   sizing — aspect-ratio alone collapses to min-content in an auto-row grid. */
.wr-swatch {
  position: relative; width: 100%; padding-top: 100%; border-radius: 10px;
  background: linear-gradient(155deg, var(--seaglass), #6fb9be);
}
.wr-swatch[data-rarity="rare"] { background: linear-gradient(155deg, var(--gold), #e8b567); }
.wr-swatch[data-rarity="epic"] { background: linear-gradient(155deg, #c9b6ff, #8a6ff0); }
.wr-swatch.is-teaser { background: linear-gradient(155deg, #2b435c, #16283b); }
/* Baked per-item thumbnail (real outfit) sits over a dark base; the rarity
   gradient above is only the placeholder shown until the bake lands. */
.wr-swatch.has-thumb {
  background-color: rgba(10, 24, 38, 0.6);
  background-repeat: no-repeat; background-size: cover; background-position: center 10%;
}
.wr-swatch-glyph { position: absolute; inset: 0; display: grid; place-items: center; font: 800 20px 'Anton', 'Montserrat'; color: rgba(253, 246, 227, 0.75); }
.wr-card-name { font: 700 11px 'Montserrat'; letter-spacing: 0.02em; color: var(--cream); line-height: 1.2; }
.wr-card-tag { font: 700 9.5px 'Montserrat'; letter-spacing: 0.08em; text-transform: uppercase; }
.wr-card-tag.t-price { color: var(--gold); }
.wr-card-tag.t-wearing { color: var(--seaglass); }
.wr-card-tag.t-owned { color: rgba(253, 246, 227, 0.5); }
.wr-card-tag.t-teaser { color: rgba(253, 246, 227, 0.4); letter-spacing: 0.2em; }
.wr-card.is-teaser .wr-card-name { color: rgba(253, 246, 227, 0.5); }

/* footer: meter + sticky action bar */
.wr-foot { flex: none; padding: 8px 18px calc(14px + env(safe-area-inset-bottom, 0px)); }
.wr-meter { font: 700 10px 'Montserrat'; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(253, 246, 227, 0.6); margin-bottom: 8px; }
.wr-actionbar { display: flex; align-items: center; gap: 12px; }
.wr-hint { flex: 1; min-width: 0; font: 600 10.5px/1.35 'Montserrat'; letter-spacing: 0.04em; color: rgba(255, 214, 130, 0.9); }
.wr-action {
  position: relative; overflow: hidden; flex: none; min-width: 44%;
  padding: 15px 22px; border-radius: 14px; border: none; cursor: pointer;
  font: 800 13px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--navy); background: linear-gradient(160deg, var(--seaglass), #7cc3c8);
  box-shadow: 0 8px 22px rgba(159, 212, 216, 0.3);
  transition: transform 0.14s ease, filter 0.14s ease, box-shadow 0.14s ease;
}
.wr-action:hover { filter: brightness(1.05); }
.wr-action:active { transform: translateY(1px); }
.wr-action.is-wear { background: linear-gradient(160deg, var(--gold), #e9be6f); }
.wr-action.is-wearing { background: rgba(253, 246, 227, 0.12); color: rgba(253, 246, 227, 0.7); box-shadow: none; cursor: default; }
.wr-action.is-unlock { background: linear-gradient(160deg, var(--gold), #e9be6f); }
.wr-action.is-confirm { background: linear-gradient(160deg, #ffd98a, #f0b24e); color: var(--navy); }
.wr-action.is-confirm::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: conic-gradient(from -90deg, rgba(18, 57, 88, 0.28) var(--sweep, 0turn), transparent 0);
  animation: wr-sweep 3s linear forwards;
}
@keyframes wr-sweep { from { --sweep: 1turn; } to { --sweep: 0turn; } }
.wr-action.is-teaser { background: rgba(253, 246, 227, 0.1); color: rgba(253, 246, 227, 0.7); box-shadow: none; }
.wr-action.is-deny { background: rgba(253, 246, 227, 0.1); color: rgba(255, 214, 130, 0.95); box-shadow: none; }
.wr-action.shake { animation: wr-shake 0.34s ease; }
@keyframes wr-shake { 10%,90%{transform:translateX(-1px);} 30%,70%{transform:translateX(3px);} 50%{transform:translateX(-4px);} }
/* @property lets the confirm sweep animate; degrades to a static tint without it. */
@property --sweep { syntax: '<angle>'; inherits: false; initial-value: 0turn; }

/* handle — right-edge half-pill entry point */
.wr-handle {
  position: absolute; top: 50%; right: 0; transform: translateY(-50%);
  width: 44px; height: 72px; border-radius: 14px 0 0 14px;
  border-right: none; color: var(--cream); cursor: pointer;
  display: grid; place-items: center; pointer-events: auto;
  transition: transform 0.2s cubic-bezier(0.22, 1.1, 0.3, 1), width 0.2s ease;
}
.wr-handle svg { width: 24px; height: 24px; }
.wr-handle:hover { width: 50px; }
.wr-handle.is-open { transform: translateY(-50%) translateX(2px); }
.wr-handle[hidden] { display: none; }
.wr-handle.wr-shine { animation: wr-shine 1.6s ease 2; }
@keyframes wr-shine {
  0% { box-shadow: inset 0 1px 0 rgba(253,246,227,0.1), 0 10px 30px rgba(6,18,30,0.35); }
  40% { box-shadow: inset 0 1px 0 rgba(253,246,227,0.1), 0 0 22px 4px rgba(255, 230, 184, 0.6); }
  100% { box-shadow: inset 0 1px 0 rgba(253,246,227,0.1), 0 10px 30px rgba(6,18,30,0.35); }
}

/* focus-visible — 2px gold, everywhere interactive */
.wr-card:focus-visible, .wr-tab:focus-visible, .wr-chip:focus-visible,
.wr-action:focus-visible, .wr-close:focus-visible, .wr-arrow:focus-visible,
.wr-handle:focus-visible, .wr-balance:focus-visible {
  outline: 2px solid var(--gold); outline-offset: 2px;
}

/* ---- portrait phone: bottom sheet ---------------------------------------- */
@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  .wr-panel {
    top: auto; bottom: 0; right: 0; left: 0; width: 100%;
    height: min(88svh, calc(100svh - 56px));
    border-radius: 18px 18px 0 0;
    transform: translateY(102%);
    transition: transform 0.3s cubic-bezier(0.4, 0, 1, 1);
  }
  .wr-root.open .wr-panel { transform: translateY(0); transition: transform 0.42s cubic-bezier(0.22, 1.18, 0.3, 1); }
  .wr-nub {
    display: block; flex: none; width: 42px; height: 5px; border-radius: 3px;
    margin: 8px auto 2px; background: rgba(253, 246, 227, 0.4);
    touch-action: none; cursor: grab;
  }
  .wr-head { padding-top: 6px; }
  /* Preview is a fixed band; the grid does the scrolling. The detail caption
     overlays the bottom of that band (stage is the positioning context). */
  .wr-stage { flex-direction: column; position: relative; }
  .wr-preview { flex: none; width: 100%; aspect-ratio: auto; height: 34svh; }
  .wr-detail {
    position: absolute; left: 0; right: 0; bottom: 0; gap: 4px; padding: 26px 18px 10px;
    background: linear-gradient(0deg, rgba(8, 20, 34, 0.92) 40%, transparent);
    pointer-events: none;
  }
  .wr-name { font-size: 19px; }
  .wr-flavor { display: none; }
  .wr-chips { position: sticky; top: 0; z-index: 1; }
  .wr-grid { grid-template-columns: repeat(3, 1fr); }
  .wr-foot {
    position: sticky; bottom: 0; z-index: 1;
    background: linear-gradient(0deg, rgba(10, 24, 38, 0.96), rgba(10, 24, 38, 0.82));
    border-top: 1px solid var(--glass-brd);
    min-height: 52px;
  }
  /* coarse tap targets */
  .wr-close, .wr-arrow { width: 48px; height: 48px; }
  .wr-tab, .wr-chip, .wr-shape-chip { min-height: 44px; }
  .wr-shape { flex-wrap: wrap; }
  /* 44px hit area with a 28px visible dot (content-box clip); wraps if needed. */
  .wr-ribbon { flex-wrap: wrap; }
  .wr-ribbon-sw { width: 44px; height: 44px; padding: 8px; background-clip: content-box; border-color: transparent; }
  .wr-ribbon-sw.selected { box-shadow: inset 0 0 0 3px var(--cream); }

  /* handle rehomed to the top-right column, the 56px slot below #glimmerCounter
     (portrait stack: met 14 → travel 68 → glimmers 126 → wardrobe 182). */
  .wr-handle {
    top: calc(182px + env(safe-area-inset-top, 0px)); right: calc(env(safe-area-inset-right, 0px) + 14px);
    left: auto; bottom: auto; transform: none;
    width: 48px; height: 48px; border-radius: 14px;
  }
  .wr-handle:hover { width: 48px; }
  .wr-handle.is-open { transform: none; }
}

/* ---- landscape phone: narrower drawer, handle above the jump/photo column - */
@media (orientation: landscape) and (hover: none) and (pointer: coarse) {
  .wr-panel { width: min(60vw, 480px); }
  .wr-preview { min-height: 150px; }
  .wr-handle {
    top: auto; bottom: calc(env(safe-area-inset-bottom, 0px) + 182px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
    transform: none; width: 48px; height: 48px; border-radius: 14px 0 0 14px;
  }
  .wr-handle.is-open { transform: none; }
}

/* ---- reduced motion: opacity fade, no slide/turntable/stagger/shine ------- */
@media (prefers-reduced-motion: reduce) {
  .wr-panel { transform: none; opacity: 0; transition: opacity 0.15s ease, visibility 0s linear 0.15s; }
  .wr-root.open .wr-panel { transform: none; opacity: 1; transition: opacity 0.15s ease; }
  .wr-root.open .wr-card { animation: none; }
  .wr-grid.wr-xfade { animation: none; }
  .wr-action.is-confirm::after { animation: none; }
  .wr-handle.wr-shine { animation: none; }
  .wr-action.shake { animation: none; }
}

/* -- map --------------------------------------------------------------------
   The Valley Map overlay. Owner: map-builder. Selectors: #map, .mp-*.
   z-index 34 (registry: reticle 28, cinema/netDiag 30, wardrobe 32, MAP 34,
   tapVeil 36, title 40, achievements 60) — a peer of the wardrobe panel, above
   it so opening the map lays over a lingering wardrobe close. Inert/no-DOM under
   det/shot (the module returns a stub). No per-frame cost while closed. */
#map { position: fixed; inset: 0; z-index: 34; pointer-events: none; }

.mp-scrim {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle at 50% 40%, rgba(6, 18, 30, 0.42), rgba(6, 18, 30, 0.72));
  opacity: 0; transition: opacity 0.26s ease;
}
#map.open .mp-scrim { opacity: 1; pointer-events: auto; }

/* Desktop: a centred glass card. Mobile portrait override sheets it below. */
.mp-sheet {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -46%) scale(0.98);
  width: min(560px, calc(100vw - 40px));
  max-height: min(88vh, 720px);
  display: flex; flex-direction: column; gap: 12px;
  padding: 18px 18px 16px; border-radius: 20px;
  pointer-events: auto; opacity: 0;
  transition: opacity 0.26s ease, transform 0.26s cubic-bezier(0.2, 0.9, 0.3, 1);
  color: var(--cream);
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.82), rgba(8, 24, 40, 0.74));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 24px 60px rgba(6, 18, 30, 0.5);
  backdrop-filter: blur(16px) saturate(1.45); -webkit-backdrop-filter: blur(16px) saturate(1.45);
}
#map.open .mp-sheet { opacity: 1; transform: translate(-50%, -50%) scale(1); }
#map.closing .mp-sheet { opacity: 0; transform: translate(-50%, -46%) scale(0.98); }

.mp-nub { display: none; }

.mp-head { display: flex; align-items: center; gap: 10px; }
.mp-title {
  flex: 1; font: 400 22px 'Anton', Impact, sans-serif; letter-spacing: 0.04em;
  color: var(--cream); text-transform: uppercase; line-height: 1;
}
.mp-close {
  width: 40px; height: 40px; flex: none; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; color: var(--cream); font-size: 17px;
  background: rgba(253, 246, 227, 0.06); border: 1px solid rgba(253, 246, 227, 0.18);
  transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.mp-close:hover { transform: scale(1.06); border-color: rgba(253, 246, 227, 0.5); }
.mp-close:active { transform: scale(0.92); }

/* the chart: a square well holding the baked terrain + the marker layer */
.mp-stage {
  position: relative; width: 100%; aspect-ratio: 1 / 1;
  border-radius: 14px; overflow: hidden;
  border: 1px solid rgba(253, 246, 227, 0.14);
  box-shadow: inset 0 0 0 1px rgba(6, 18, 30, 0.35), inset 0 2px 20px rgba(6, 18, 30, 0.4);
  background: #10314c;
}
.mp-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.mp-markers { position: absolute; inset: 0; pointer-events: none; }

/* plaza pins — gold dot + small-caps label. Zero-size box anchors on the coord. */
.mp-pin, .mp-spawn, .mp-you, .mp-peer {
  position: absolute; width: 0; height: 0; transform: translate(-50%, -50%);
}
.mp-pin-dot {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 11px; height: 11px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #fff5d8, var(--gold));
  border: 1.5px solid rgba(18, 57, 88, 0.85);
  box-shadow: 0 1px 4px rgba(6, 18, 30, 0.6), 0 0 8px rgba(255, 230, 184, 0.5);
}
.mp-pin-label {
  position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  white-space: nowrap; color: var(--cream);
  font: 700 9.5px 'Montserrat'; letter-spacing: 0.09em; text-transform: uppercase;
  text-shadow: 0 1px 3px rgba(6, 18, 30, 0.95), 0 0 6px rgba(6, 18, 30, 0.7);
}
.mp-pin.flip .mp-pin-label { left: auto; right: 10px; }

/* spawn — a soft cream ring, quieter than the gold plazas */
.mp-spawn-dot {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 9px; height: 9px; border-radius: 50%;
  background: rgba(253, 246, 227, 0.85); border: 1.5px solid rgba(18, 57, 88, 0.8);
  box-shadow: 0 1px 3px rgba(6, 18, 30, 0.5);
}
.mp-spawn-label {
  position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
  white-space: nowrap; color: rgba(253, 246, 227, 0.82);
  font: 600 8.5px 'Montserrat'; letter-spacing: 0.08em; text-transform: uppercase;
  text-shadow: 0 1px 3px rgba(6, 18, 30, 0.95);
}

/* other explorers — warm live dots, colour = their player accent (--pc) */
.mp-peer {
  width: 9px; height: 9px; border-radius: 50%; margin: 0;
  transform: translate(-50%, -50%);
  background: var(--pc, #7ef0b2);
  border: 1.5px solid rgba(253, 246, 227, 0.9);
  box-shadow: 0 1px 4px rgba(6, 18, 30, 0.55);
  transition: left 0.2s linear, top 0.2s linear;
}

/* YOU — a seaglass beacon with a heading arrow; the ring pulses gently */
.mp-you { z-index: 3; }
.mp-you-arrow {
  position: absolute; left: 0; top: 0; width: 20px; height: 20px;
  transform-origin: 50% 50%; /* JS sets rotate(); translate handled by parent box */
  margin-left: -10px; margin-top: -10px;
  fill: var(--seaglass);
  filter: drop-shadow(0 1px 3px rgba(6, 18, 30, 0.8));
}
.mp-you-ring {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid rgba(159, 212, 216, 0.7);
  box-shadow: 0 0 0 0 rgba(159, 212, 216, 0.45);
  animation: mp-ping 2.4s ease-out infinite;
}
@keyframes mp-ping {
  0%   { box-shadow: 0 0 0 0 rgba(159, 212, 216, 0.45); }
  70%  { box-shadow: 0 0 0 10px rgba(159, 212, 216, 0); }
  100% { box-shadow: 0 0 0 0 rgba(159, 212, 216, 0); }
}

/* touch entry button (#mapBtn, created by map.js). Desktop uses the M key, so
   this shows only under a coarse pointer, in the portrait right column's next
   56px slot below the wardrobe handle (met 14 → travel 68 → glimmer 126 →
   wardrobe 182 → MAP 238). Glass language + 48px coarse target. */
#mapBtn {
  position: absolute; display: none; place-items: center; z-index: 21;
  width: 48px; height: 48px; border-radius: 14px; cursor: pointer; color: var(--cream);
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.78), rgba(8, 24, 40, 0.68));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35), 0 6px 18px rgba(6, 18, 30, 0.4);
  backdrop-filter: blur(14px) saturate(1.45); -webkit-backdrop-filter: blur(14px) saturate(1.45);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
#mapBtn[hidden] { display: none; }
#mapBtn svg { width: 22px; height: 22px; display: block; filter: drop-shadow(0 1px 2px rgba(6, 18, 30, 0.6)); }
#mapBtn:active { transform: scale(0.92); border-color: rgba(253, 246, 227, 0.5); }
@media (hover: none) and (pointer: coarse) {
  #mapBtn {
    display: grid;
    top: calc(env(safe-area-inset-top, 0px) + 238px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
  }
}
/* Landscape phone: the right column stacks upward from the bottom (photo 70 →
   jump 126 → wardrobe 182 → MAP 238), matching the wardrobe handle's rehome. */
@media (orientation: landscape) and (hover: none) and (pointer: coarse) {
  #mapBtn {
    top: auto; bottom: calc(env(safe-area-inset-bottom, 0px) + 238px);
    right: calc(env(safe-area-inset-right, 0px) + 14px);
  }
}

/* legend */
.mp-legend { display: flex; flex-wrap: wrap; gap: 8px 16px; padding: 2px 2px 0; }
.mp-leg { display: flex; align-items: center; gap: 7px; }
.mp-leg-txt { font: 600 10px 'Montserrat'; letter-spacing: 0.06em; color: rgba(253, 246, 227, 0.78); text-transform: uppercase; }
.mp-leg-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.mp-leg-dot.is-you { background: var(--seaglass); border: 1.5px solid rgba(253, 246, 227, 0.85); }
.mp-leg-dot.is-plaza { background: radial-gradient(circle at 35% 30%, #fff5d8, var(--gold)); border: 1.5px solid rgba(18, 57, 88, 0.85); }
.mp-leg-dot.is-peer { background: #7ef0b2; border: 1.5px solid rgba(253, 246, 227, 0.9); }

/* Mobile portrait: a full-width bottom sheet with safe areas + a grab nub. */
@media (orientation: portrait) and (hover: none) and (pointer: coarse) {
  .mp-sheet {
    left: 0; top: auto; bottom: 0; right: 0;
    width: auto; max-height: 92vh;
    transform: translateY(6%);
    border-radius: 22px 22px 0 0;
    padding: 8px 18px calc(16px + env(safe-area-inset-bottom, 0px));
    padding-left: calc(18px + env(safe-area-inset-left, 0px));
    padding-right: calc(18px + env(safe-area-inset-right, 0px));
  }
  #map.open .mp-sheet { transform: translateY(0); }
  #map.closing .mp-sheet { transform: translateY(6%); }
  .mp-nub {
    display: block; width: 42px; height: 5px; border-radius: 3px; margin: 4px auto 2px;
    background: rgba(253, 246, 227, 0.3); touch-action: none;
  }
  .mp-close { width: 44px; height: 44px; }
}

@media (prefers-reduced-motion: reduce) {
  /* Opacity-only fade; hold the final position so nothing slides or scales. */
  .mp-scrim, .mp-sheet { transition: opacity 0.14s ease; }
  .mp-sheet, #map.open .mp-sheet, #map.closing .mp-sheet { transform: translate(-50%, -50%); }
  .mp-you-ring { animation: none; }
  .mp-peer { transition: none; }
}
@media (orientation: portrait) and (hover: none) and (pointer: coarse) and (prefers-reduced-motion: reduce) {
  .mp-sheet, #map.open .mp-sheet, #map.closing .mp-sheet { transform: translateY(0); }
}

/* ============================================================ worlds ======
   Directory surfaces (worlds-lobby, task #90): the title live line and the
   in-game Worlds panel. Own selectors only — no other module is touched. */

/* Title screen — "N explorers are in the valley right now". Navy on the light
   cover, sits under the name/Enter stack, filled in by ui/worlds.js. It rides
   #titleUi's opacity fade, so it never blocks first paint. */
#titlePresence {
  margin: 2px 0 0; max-width: 320px; text-align: center;
  font: 700 11px 'Montserrat', sans-serif; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(18, 57, 88, 0.82); text-shadow: 0 1px 0 rgba(253, 246, 227, 0.5);
}
#titlePresence[hidden] { display: none; }

/* In-game globe button — mid-left rail, clear of the bottom button clusters and
   the touch stick. Glass recipe, matched to the other HUD buttons. */
#worldsBtn {
  position: fixed; z-index: 26; left: 14px; top: 46%;
  width: 44px; height: 44px; display: grid; place-items: center; cursor: pointer;
  border-radius: 50%; color: var(--cream);
  background: linear-gradient(160deg, rgba(20, 44, 68, 0.78), rgba(8, 24, 40, 0.68));
  border: 1px solid rgba(253, 246, 227, 0.16);
  box-shadow: inset 0 1px 0 rgba(253, 246, 227, 0.1), 0 10px 30px rgba(6, 18, 30, 0.35);
  backdrop-filter: blur(14px) saturate(1.45); -webkit-backdrop-filter: blur(14px) saturate(1.45);
  transition: transform 0.16s ease, color 0.16s ease;
}
#worldsBtn:hover { transform: translateY(-1px); }
#worldsBtn.open { color: var(--gold); }
#worldsBtn svg { width: 24px; height: 24px; display: block; }
#worldsBtn[hidden] { display: none; }

/* The panel — a compact glass list anchored above the globe button. */
#worldsPanel {
  position: fixed; z-index: 30; left: 14px; top: calc(46% + 54px);
  width: 260px; max-width: calc(100vw - 28px); padding: 14px; border-radius: 16px;
  color: var(--cream);
  background: linear-gradient(165deg, rgba(20, 46, 72, 0.94), rgba(9, 26, 42, 0.92));
  border: 1px solid rgba(253, 246, 227, 0.2); box-shadow: 0 24px 60px rgba(4, 12, 22, 0.55);
  backdrop-filter: blur(14px) saturate(1.45); -webkit-backdrop-filter: blur(14px) saturate(1.45);
}
#worldsPanel[hidden] { display: none; }
.wl-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.wl-kicker { font: 700 10px 'Montserrat'; letter-spacing: 0.34em; text-transform: uppercase; color: var(--gold); }
.wl-close {
  width: 26px; height: 26px; border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(253, 246, 227, 0.1); color: var(--cream); font-size: 13px; line-height: 1;
}
.wl-close:hover { background: rgba(253, 246, 227, 0.2); }
.wl-list { display: flex; flex-direction: column; gap: 8px; }
.wl-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 12px;
  background: rgba(253, 246, 227, 0.06); border: 1px solid rgba(253, 246, 227, 0.1);
}
.wl-row.active { border-color: rgba(255, 230, 184, 0.34); background: rgba(255, 230, 184, 0.08); }
.wl-copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.wl-name { font: 700 13px 'Montserrat'; letter-spacing: 0.02em; color: var(--cream); }
.wl-meta { font: 600 10.5px 'Montserrat'; letter-spacing: 0.04em; color: rgba(253, 246, 227, 0.7); }
.wl-join {
  flex: none; min-width: 52px; padding: 7px 12px; border-radius: 999px; cursor: pointer; border: 0;
  color: var(--navy); background: var(--gold);
  font: 800 9px 'Montserrat'; letter-spacing: 0.11em; text-transform: uppercase;
  transition: transform 0.14s ease, filter 0.14s ease;
}
.wl-join:hover { transform: scale(1.04); filter: brightness(1.05); }
.wl-here { flex: none; font: 700 9px 'Montserrat'; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255, 230, 184, 0.85); }
.wl-foot { margin: 12px 0 0; font: 500 10.5px/1.4 'Montserrat'; color: rgba(253, 246, 227, 0.55); }

/* =================================================================== race == */
/* A pure view over game/races.js (src/ui/race.js). Top-center instrument tier
   #raceHud (z 22) — the only unclaimed HUD zone; it never enters the presence
   (top-left), met/travel/glimmer/wardrobe/map (top-right column), announcement,
   or bottom-bar zones. Countdown + lobby ride z 22; the finish card sits z 33,
   just below the map tier. Palette: navy glass, cream type, gold + seaglass.
   Anton for numerals, Montserrat for labels. */

#raceHud {
  position: fixed; inset: 0; z-index: 22; pointer-events: none;
  display: flex; flex-direction: column; align-items: center;
  /* Top-center column, clear of the presence pill (top-left) and the met/travel
     column (top-right). Safe-area aware for notched portrait. */
  padding-top: calc(20px + env(safe-area-inset-top, 0px));
  gap: 6px; text-align: center; color: var(--cream);
}
#raceHud[hidden] { display: none; }

/* Never-lost chevron — points at the next ring. Rotation is information, so it
   survives reduced-motion; only the pulse is dropped there. */
#raceNext {
  display: inline-flex; align-items: center; gap: 8px;
  font: 700 11px 'Montserrat'; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--gold); text-shadow: 0 2px 14px rgba(9, 24, 38, 0.85);
  transition: opacity 0.3s ease;
}
#raceNext .rn-arrow {
  width: 0; height: 0; display: inline-block;
  border-left: 7px solid transparent; border-right: 7px solid transparent;
  border-bottom: 12px solid var(--gold);
  filter: drop-shadow(0 2px 6px rgba(9, 24, 38, 0.7));
  transition: transform 0.18s ease;
}
/* On-screen next ring: the in-world armed ring carries the cue, so fade the
   chevron back to a hint. */
#raceNext.near { opacity: 0.32; }
/* Wrong-way: warm coral nudge, no penalty. */
#raceNext.wrong { color: #ffb59b; }
#raceNext.wrong .rn-arrow { border-bottom-color: #ffb59b; }

#raceTimer {
  font: 400 34px/1 'Anton'; letter-spacing: 0.04em;
  font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1;
  color: var(--cream);
  text-shadow: 0 4px 26px rgba(9, 24, 38, 0.8), 0 1px 0 rgba(9, 24, 38, 0.35);
}
#raceCP {
  font: 700 12px 'Montserrat'; letter-spacing: 0.18em; text-transform: uppercase;
  color: rgba(253, 246, 227, 0.9); text-shadow: 0 2px 12px rgba(9, 24, 38, 0.8);
}
#raceCP.tick { animation: race-cp-tick 0.55s ease; color: var(--seaglass); }
@keyframes race-cp-tick { 30% { transform: scale(1.12); } }

#raceSplit {
  font: 800 13px 'Montserrat'; letter-spacing: 0.06em;
  text-shadow: 0 2px 12px rgba(9, 24, 38, 0.85);
}
#raceSplit[hidden] { display: none; }
#raceSplit.ahead { color: var(--seaglass); }
#raceSplit.behind { color: #ffc46b; }

/* Live position — TOP-CENTER, directly under the checkpoint counter. Never the
   reserved top-right column. */
#racePos {
  display: inline-flex; align-items: center; gap: 8px;
  font: 700 12px 'Montserrat'; letter-spacing: 0.1em;
  color: var(--cream); text-shadow: 0 2px 12px rgba(9, 24, 38, 0.85);
}
#racePos[hidden] { display: none; }
#racePos .rp-place { color: var(--gold); font-weight: 800; }
#racePos .rp-of { opacity: 0.7; font-weight: 600; }
#racePos .rp-dots { display: inline-flex; align-items: center; }
#racePos .rp-dots b {
  width: 9px; height: 9px; border-radius: 50%; margin-left: -3px;
  border: 1.5px solid rgba(9, 24, 38, 0.5);
}
#racePos .rp-dots b:first-child { margin-left: 0; }
#racePos .rp-dots b.me { border-color: var(--cream); box-shadow: 0 0 0 1.5px var(--cream); }

/* Racing-mode world dim — on <html> so it reaches both the #hud tree and the
   sibling #raceHud tree. Quiets the ambient counters while a run is live. */
html.racing #metCounter, html.racing #glimmerCounter { opacity: 0.28; pointer-events: none; }

/* -- countdown 3·2·1·GO ---------------------------------------------------- */
#raceCountdown {
  position: fixed; inset: 0; z-index: 22; pointer-events: none;
  display: grid; place-items: center;
  background: radial-gradient(ellipse 60% 50% at 50% 45%, rgba(9, 24, 38, 0.42), rgba(9, 24, 38, 0.12) 70%, transparent);
}
#raceCountdown[hidden] { display: none; }
#raceCountdown .rc-num {
  font: 400 clamp(96px, 26vw, 200px)/1 'Anton'; letter-spacing: 0.02em; color: var(--cream);
  text-shadow: 0 8px 60px rgba(9, 24, 38, 0.7), 0 2px 0 rgba(9, 24, 38, 0.4);
}
#raceCountdown.go .rc-num { color: var(--gold); }
#raceCountdown .rc-ring {
  position: absolute; width: min(52vw, 320px); height: min(52vw, 320px);
  border-radius: 50%; border: 2px solid rgba(159, 212, 216, 0.5);
}
#raceCountdown.beat .rc-num { animation: rc-pop 0.5s cubic-bezier(0.2, 1.4, 0.3, 1); }
#raceCountdown.beat .rc-ring { animation: rc-ping 0.7s ease-out; }
@keyframes rc-pop { from { opacity: 0; transform: scale(1.5); } 50% { opacity: 1; } }
@keyframes rc-ping { from { transform: scale(0.4); opacity: 0.8; } to { transform: scale(1.4); opacity: 0; } }

/* -- live-race lobby (lower sheet) ---------------------------------------- */
#raceLobby {
  position: fixed; z-index: 22; pointer-events: auto;
  left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%);
  width: min(420px, calc(100vw - 32px));
  padding: 18px 20px; border-radius: 20px; color: var(--cream);
  opacity: 0; transition: opacity 0.32s ease, transform 0.32s ease;
}
#raceLobby[hidden] { display: none; }
#raceLobby.in { opacity: 1; transform: translateX(-50%) translateY(0); }
#raceLobby.out { opacity: 0; transform: translateX(-50%) translateY(14px); }
.rl-title { display: block; font: 400 20px 'Anton'; letter-spacing: 0.06em; color: var(--gold); text-transform: uppercase; }
.rl-list { margin: 12px 0; display: flex; flex-direction: column; gap: 7px; }
.rl-row { display: flex; align-items: center; gap: 10px; font: 600 12px 'Montserrat'; }
.rl-row.me { color: var(--gold); }
.rl-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; border: 1.5px solid rgba(253, 246, 227, 0.8); }
.rl-name { flex: 1; text-align: left; }
.rl-name em { opacity: 0.7; font-style: normal; font-weight: 500; }
.rl-flag { font: 700 9px 'Montserrat'; letter-spacing: 0.12em; text-transform: uppercase; opacity: 0.6; }
.rl-flag.on { color: var(--seaglass); opacity: 1; }
.rl-actions { display: flex; gap: 10px; align-items: center; }
.rl-btn {
  min-height: 44px; padding: 0 20px; border-radius: 999px; cursor: pointer;
  color: var(--cream); font: 700 12px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
  transition: transform 0.14s ease, filter 0.14s ease, border-color 0.14s ease;
}
.rl-ready { flex: none; }
.rl-ready.on { border-color: var(--seaglass); color: var(--seaglass); }
.rl-solo { flex: 1; background: none; border-color: transparent; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none;
  text-transform: none; font-weight: 600; letter-spacing: 0.02em; color: rgba(253, 246, 227, 0.6); }
.rl-btn:hover { transform: scale(1.03); filter: brightness(1.06); }
.rl-solo:hover { color: var(--cream); }
.rl-foot { display: block; margin-top: 10px; font: 500 10.5px 'Montserrat'; color: rgba(253, 246, 227, 0.5); letter-spacing: 0.02em; }

/* -- finish card (lower sheet, z 33) -------------------------------------- */
#raceFinish {
  position: fixed; z-index: 33; pointer-events: auto;
  left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px)); transform: translateX(-50%) translateY(16px);
  width: min(420px, calc(100vw - 32px));
  padding: 20px 22px; border-radius: 22px; color: var(--cream);
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  opacity: 0; transition: opacity 0.42s ease, transform 0.42s cubic-bezier(0.2, 0.9, 0.25, 1);
}
#raceFinish[hidden] { display: none; }
#raceFinish.in { opacity: 1; transform: translateX(-50%) translateY(0); }
#raceFinish.out { opacity: 0; transform: translateX(-50%) translateY(16px); }
.rf-kicker { font: 700 11px 'Montserrat'; letter-spacing: 0.28em; text-transform: uppercase; color: rgba(253, 246, 227, 0.8); }
.rf-time { font: 400 46px/1 'Anton'; letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
  color: var(--cream); text-shadow: 0 4px 30px rgba(9, 24, 38, 0.6); margin: 2px 0; }
.rf-meta { font: 600 12px 'Montserrat'; letter-spacing: 0.04em; color: rgba(253, 246, 227, 0.82); }
.rf-reward { font: 800 13px 'Montserrat'; letter-spacing: 0.06em; color: var(--gold); margin-top: 2px; }
.rf-reward[hidden] { display: none; }
.rf-actions { display: flex; gap: 10px; margin-top: 14px; width: 100%; }
.rf-btn {
  flex: 1; min-height: 44px; border-radius: 999px; cursor: pointer;
  color: var(--cream); font: 700 12px 'Montserrat'; letter-spacing: 0.1em; text-transform: uppercase;
  transition: transform 0.14s ease, filter 0.14s ease;
}
.rf-primary { color: var(--gold); border-color: rgba(255, 230, 184, 0.4); }
.rf-ghost { background: none; box-shadow: none; backdrop-filter: none; -webkit-backdrop-filter: none;
  border-color: rgba(253, 246, 227, 0.2); color: rgba(253, 246, 227, 0.75); }
.rf-btn:hover { transform: scale(1.03); filter: brightness(1.07); }
/* PB celebration — gold shimmer sweep across the card. */
#raceFinish.pb { box-shadow: inset 0 1px 0 rgba(255, 230, 184, 0.3), 0 10px 40px rgba(255, 214, 120, 0.25); }
#raceFinish.pb .rf-time { color: var(--gold); animation: rf-shimmer 1.4s ease; }
@keyframes rf-shimmer { 0% { filter: brightness(1); } 40% { filter: brightness(1.5) drop-shadow(0 0 18px rgba(255, 230, 184, 0.7)); } }
/* Unverified (sub-floor) run — shown, but muted: no gold, no celebration. */
#raceFinish.unverified .rf-time { color: rgba(253, 246, 227, 0.62); }
#raceFinish.unverified .rf-meta { color: rgba(255, 196, 107, 0.85); }

/* -- portrait: stack the top-center readouts tighter --------------------- */
@media (orientation: portrait) {
  #raceTimer { font-size: 30px; }
  .rf-time { font-size: 40px; }
}
@media (max-width: 380px) {
  #raceHud { gap: 4px; }
  #raceTimer { font-size: 27px; }
}

/* -- reduced motion: kill pulse/shimmer/ripple, keep chevron rotation ----- */
@media (prefers-reduced-motion: reduce) {
  #raceCP.tick, #raceCountdown.beat .rc-num, #raceCountdown.beat .rc-ring,
  #raceFinish.pb .rf-time { animation: none; }
  #raceNext .rn-arrow { transition: none; }   /* still rotates — it's information */
}
