/* deceit.party shared shell and Play lobby. Static, native game art and fonts only.
   Shared with /skins/: fonts, tokens, base, skip link, a plain .btn base and the top bar.
   Everything else is scoped to body.page-play, because the Skins app reuses names such
   as .stage, .face and .title.
   Play is the Deceit 1 Live lobby: one full-screen room (#lobby-stage, filled by the
   root-owned scene), a large white menu at the bottom left, the character name at the
   bottom right and the party rail on the right. Join, Host, Download and Help open as
   lobby panels drawn like the native Private Match window: near-black, a thin red frame,
   wide uppercase titles and square tiles. Sizes follow the native 16:9 captures through
   one unit, --u, so the composition keeps its ratios on any desktop screen.
   The same document also holds the native loading screen (#lobby-loading), the Party
   confetti layer (#party-confetti) and the Equipment view (#equipment-view), which root
   shows by switching body.page-play to body.page-equipment.
   Stacking, bottom to top: room 0, lobby and Equipment (auto), loading 30, header 50,
   confetti 55, open panel 60. */

/* The lobby fonts, taken from the game's own Scaleform font file: Montserrat in three
   weights plus Bold Italic, and the small UI face (Questrial). */
@font-face { font-family: "Deceit"; src: url("/lobby/font-medium.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Deceit"; src: url("/lobby/font-bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Deceit"; src: url("/lobby/font-black.woff2") format("woff2"); font-weight: 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Deceit"; src: url("/lobby/font-bold-italic.woff2") format("woff2"); font-weight: 700; font-style: italic; font-display: swap; }
@font-face { font-family: "Deceit Small"; src: url("/lobby/font-regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }

:root {
  color-scheme: dark;
  --bg: #0e0d0d;
  --surface: #161414;
  --ink: #f2efed;
  --ink-2: #bdb6b2;
  --ink-3: #948c88;
  --line: rgba(255, 255, 255, .09);
  --line-2: rgba(255, 255, 255, .17);
  --red: #c8382a;
  --red-hi: #d9452f;
  --red-mark: #e0402f;
  --ok: #8ddcae;
  --bad: #ff8f7c;
  --ring: #ffc2b3;
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-arrive: cubic-bezier(0.16, 1, 0.3, 1);
  --font: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
  /* Bahnschrift ships with Windows, like every player's PC, and matches the launcher. */
  --display: Bahnschrift, "DIN Condensed", "DIN Alternate", "Roboto Condensed", "Arial Narrow", sans-serif;
  --mono: ui-monospace, "Cascadia Mono", Consolas, "SFMono-Regular", Menlo, monospace;
  /* The lobby faces: Montserrat for menus, names and keys, Questrial for small print. */
  --lobby: "Deceit", "Montserrat", Arial, sans-serif;
  --lobby-small: "Deceit Small", "Questrial", Arial, sans-serif;
  --gutter: max(16px, env(safe-area-inset-left), env(safe-area-inset-right));
  --wrap: min(1240px, calc(100% - 2 * var(--gutter)));
  --shell-h: 52px;
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html { scroll-padding-top: 88px; -webkit-text-size-adjust: 100%; -webkit-tap-highlight-color: transparent; }
body {
  margin: 0;
  min-height: 100vh;
  color: var(--ink);
  font: 400 1rem/1.55 var(--font);
  -webkit-font-smoothing: antialiased;
  background: var(--bg);
}
img { max-width: 100%; }
h1, h2, h3 { margin: 0; font-weight: 750; letter-spacing: -.03em; line-height: 1.05; }
h2 { font-size: clamp(1.9rem, 3.8vw, 2.9rem); }
p { margin: 0; }
strong { font-weight: 700; color: var(--ink); }
:focus-visible { outline: 3px solid var(--ring); outline-offset: 3px; }
main:focus { outline: none; }

.skip {
  position: absolute;
  left: 12px;
  top: -80px;
  z-index: 200;
  padding: 12px 16px;
  background: var(--ink);
  color: #171313;
  font-weight: 700;
}
.skip:focus { top: max(12px, env(safe-area-inset-top)); }

/* Plain button base. Skins styles its own #app .btn; Play styles the viewer's buttons. */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font: inherit;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}
.btn:active { transform: scale(.97); }

/* ---------- Top bar (shared with /skins/) ----------
   A thin dark strip in the lobby face: square edges, uppercase tabs, one red rule under
   the current feature. On Play it floats over the room so the scene never moves; on
   Skins and in the Equipment view it stays in the flow (skins/src/style.css sets it
   relative).
   Horizontal spacing lives in five variables so each width tier only retunes them. */
.topbar {
  --bar-x: 20px;
  --bar-gap: 8px;
  --nav-start: 24px;
  --tab-x: 14px;
  --get-x: 18px;
  position: sticky;
  top: 0;
  z-index: 50;
  height: var(--shell-h);
  padding: 0 max(var(--bar-x), env(safe-area-inset-right)) 0 max(var(--bar-x), env(safe-area-inset-left));
  border-bottom: 1px solid rgba(255, 255, 255, .07);
  color: var(--ink);
  background: rgba(9, 10, 11, .94);
  font: 400 1rem/1.55 var(--font);
  letter-spacing: normal;
  view-transition-name: dp-shell;
}
.topbar :focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.topbar-inner {
  display: flex;
  align-items: stretch;
  gap: var(--bar-gap);
  max-width: 1920px;
  height: 100%;
  margin: 0 auto;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  color: #fff;
  font: 900 14px/1 var(--lobby);
  letter-spacing: .2em;
  text-decoration: none;
  -webkit-user-select: none;
  user-select: none;
}
.topbar .brand { align-self: center; }
.brand-tag { color: #e2584a; font: 700 13px/1 var(--lobby); letter-spacing: .02em; }
/* The red dot. Holding it with a mouse is the blackout Easter egg (feel.mjs). */
.brand-mark {
  position: relative;
  flex: none;
  width: 8px;
  height: 8px;
  margin-right: 4px;
  border-radius: 50%;
  background: var(--red-mark);
  transition: transform 200ms var(--ease-out);
}
.site-nav { display: flex; flex: 1; align-items: stretch; min-width: 0; margin-left: var(--nav-start); }
.site-links { display: flex; margin-left: auto; }
.site-nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--tab-x);
  color: #a7a9ab;
  font: 700 13px/1 var(--lobby);
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
  transition: color 150ms ease;
}
.site-nav a::after {
  content: "";
  position: absolute;
  left: var(--tab-x);
  right: var(--tab-x);
  bottom: 13px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 200ms var(--ease-out);
}
.site-tab[aria-current="page"] { color: #fff; }
.site-tab[aria-current="page"]::after {
  bottom: -1px;
  height: 2px;
  background: var(--red-mark);
  transform: none;
  view-transition-name: dp-tab;
}
/* Download: the native gray action key (Start Match), square and raised. */
.get {
  flex: none;
  align-self: center;
  display: inline-flex;
  align-items: center;
  height: 44px;
  padding: 0 var(--get-x);
  border: 1px solid rgba(255, 255, 255, .2);
  color: #f4f4f4;
  background: linear-gradient(180deg, #4d4d4f, #39393b);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12);
  font: 700 13px/1 var(--lobby);
  letter-spacing: .16em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
  transition: border-color 150ms ease, transform 120ms var(--ease-out);
}
.get:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) {
  .site-nav a:hover { color: #fff; }
  .site-nav a:not([aria-current="page"]):hover::after { transform: scaleX(1); transform-origin: left; }
  .get:hover { border-color: rgba(255, 255, 255, .42); }
  .brand:hover .brand-mark { transform: scale(1.4); }
}
/* Equipment is the longest tab, so Join and Host leave first, then Help and the tag.
   Measured in the lobby Montserrat: everything needs about 770px; brand, Play, Equipment,
   Help and Download 630px; the same without Help or the tag 455px. */
@media (max-width: 780px) { .site-links .nav-wide { display: none; } }
@media (max-width: 640px) {
  .topbar { --bar-x: 14px; --nav-start: 6px; --tab-x: 10px; --get-x: 12px; }
  .brand-tag, .site-links { display: none; }
}
@media (max-width: 460px) {
  .topbar { --bar-x: 10px; --bar-gap: 2px; --nav-start: 0px; --tab-x: 6px; --get-x: 9px; }
  .brand { gap: 5px; font-size: 13px; letter-spacing: .1em; }
  .site-nav a, .get { font-size: 12px; letter-spacing: .04em; }
}
/* 320px phones: brand, Play, Equipment and Download fit in about 305px. */
@media (max-width: 359px) {
  .topbar { --bar-x: 8px; --tab-x: 5px; --get-x: 8px; }
  .brand { gap: 4px; font-size: 12px; letter-spacing: .06em; }
  .site-nav a, .get { font-size: 11px; letter-spacing: 0; }
}

@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
::view-transition-group(dp-tab) { animation-duration: 250ms; animation-timing-function: var(--ease-in-out); }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 200ms; }
@media (prefers-reduced-motion: reduce) {
  .topbar *, .topbar *::before, .topbar *::after { transition-duration: 1ms !important; }
}

/* ---------- Play: page surface ---------- */
.page-play {
  --u: min(1vh, .6vw);
  --rail-w: clamp(236px, 21.7vw, 340px);
  --menu-x: calc(var(--u) * 12);
  --floor: calc(var(--u) * 7.8);
  --white: #f5f5f5;
  --dim: #c9c9c9;
  --panel-red: #8e1212;
  --panel-hot: #d11a1a;
  --pick: #a30000;
  --gold: #d6ad57;
  --teal-0: rgba(16, 27, 35, .95);
  --teal-1: #1b2a34;
  --teal-2: #13202a;
  --green: #0b6c48;
  min-height: 100vh;
  background: #050506;
  font-family: var(--lobby);
  -webkit-tap-highlight-color: transparent;
}
:root:has(> body.page-play) { scrollbar-color: #3b3634 #050506; }
.page-play ::selection { color: #fff; background: rgba(163, 0, 0, .7); }
.page-play :focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }
.page-play img { -webkit-user-drag: none; }
.page-play :is(.menu-item, .cta, .key, .clear, .copy, .tile, .qa summary, .rail a, .panel-close, .lightbox-bar) {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}
/* The native lobby has no site bar, so on Play it is only a shade over the wall: no rule,
   dimmer tabs and a see-through Download key. Beside the rail it stops at the rail's edge. */
.page-play .topbar {
  position: fixed;
  inset: 0 0 auto;
  border-bottom: 0;
  background: linear-gradient(180deg, rgba(0, 0, 0, .62), rgba(0, 0, 0, .3) 70%, rgba(0, 0, 0, 0));
}
.page-play .site-nav a { color: rgba(214, 214, 214, .62); text-shadow: 0 1px 6px rgba(0, 0, 0, .6); }
.page-play .site-tab[aria-current="page"] { color: rgba(255, 255, 255, .92); }
.page-play .site-tab[aria-current="page"]::after { left: calc(var(--tab-x) + 4px); right: calc(var(--tab-x) + 4px); bottom: 6px; }
.page-play .get {
  border-color: rgba(255, 255, 255, .24);
  color: #e6e6e6;
  background: rgba(40, 40, 42, .55);
  box-shadow: none;
}
@media (hover: hover) and (pointer: fine) {
  .page-play .site-nav a:hover { color: #fff; }
}
.page-play .vh {
  position: absolute !important;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.page-play .noscript {
  position: absolute;
  z-index: 5;
  top: calc(var(--shell-h) + 12px);
  left: var(--gutter);
  right: var(--gutter);
  color: var(--dim);
  font-size: 14px;
}

/* ---------- The room ----------
   #lobby-stage is the one full-screen layer, and data-stage says what it shows:
   "pending" (the page's first state) shows nothing, "live" shows the scene's canvas and
   "still" shows the same room rendered with nobody in it, for when 3D is unavailable.
   The canvas stays invisible until "live", so the first frame anyone sees is the settled
   camera. The loading screen covers the change.
   The shade on top brings the web scene closer to the darker native room: a low overall
   dim, a darker upper wall, corners and floor. It stays light in the middle so the
   character reads. */
.page-play .lobby-stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: #070708;
}
.page-play .lobby-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 66% 74% at 50% 48%, rgba(0, 0, 0, 0) 38%, rgba(0, 0, 0, .55) 100%),
    linear-gradient(90deg, rgba(0, 0, 0, .4), rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, 0) 70%, rgba(0, 0, 0, .3)),
    linear-gradient(180deg, rgba(0, 0, 0, .3), rgba(0, 0, 0, .08) 30%, rgba(0, 0, 0, 0) 46%),
    linear-gradient(0deg, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0) 26%),
    rgba(0, 0, 0, .1);
}
.page-play .lobby-stage > :is(canvas, .lobby-scene) { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; }
.page-play .lobby-stage[data-stage="live"] > :is(canvas, .lobby-scene) { opacity: 1; }
.page-play .lobby-room-still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  object-position: 50% 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 200ms ease;
}
.page-play .lobby-stage[data-stage="still"] .lobby-room-still { opacity: 1; }

/* No z-index here: the open panel has to rise above the loading screen and confetti. */
.page-play .lobby {
  position: relative;
  min-height: max(100vh, 560px);
  min-height: max(100dvh, 560px);
  pointer-events: none;
}
.page-play .lobby :is(.lobby-left, .rail, .panel, .noscript) { pointer-events: auto; }

/* ---------- Left column: the menu ----------
   Native ratios at 648px tall (game area, window frame removed): menu text 78px from the
   left, the first row's top 314px above the floor, big rows on a 46px pitch and small
   rows on 31px. The menu hangs from that first row, so Play never moves when the Music
   row appears. Short or narrow screens stack from the floor. */
.page-play .lobby-left {
  position: absolute;
  left: var(--menu-x);
  bottom: var(--floor);
}
@media (min-width: 861px) and (min-height: 621px) {
  .page-play .lobby-left { top: calc(100% - var(--u) * 48.4); bottom: auto; }
}

.page-play .menu-list { margin: 0; padding: 0; list-style: none; }
.page-play .menu-list > li { width: max-content; }
.page-play .menu-item {
  position: relative;
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--white);
  background: none;
  font-family: var(--lobby);
  text-transform: uppercase;
  text-decoration: none;
  text-shadow: 0 2px 10px rgba(0, 0, 0, .55);
  white-space: nowrap;
  cursor: pointer;
  transition: color 150ms ease;
}
/* Big rows: Montserrat Bold, 27px caps on a 46px pitch at 648px. Small rows: Medium,
   17px caps on 31px, widely tracked. Touch screens keep 44px rows. */
.page-play .menu-big {
  min-height: max(44px, calc(var(--u) * 7.1));
  font-size: max(30px, calc(var(--u) * 5.95));
  font-weight: 700;
  line-height: 1;
  letter-spacing: .005em;
}
.page-play .menu-small {
  min-height: 44px;
  font-size: max(17px, calc(var(--u) * 3.7));
  font-weight: 500;
  line-height: 1;
  letter-spacing: .075em;
}
@media (pointer: fine) {
  .page-play .menu-small { min-height: max(28px, calc(var(--u) * 4.8)); }
}
/* The native hover mark: a small gold dot left of the item. */
.page-play .menu-item::before {
  content: "";
  position: absolute;
  top: 50%;
  left: calc(var(--u) * -2.8);
  width: max(6px, calc(var(--u) * 1.1));
  height: max(6px, calc(var(--u) * 1.1));
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 8px rgba(214, 173, 87, .55);
  opacity: 0;
  transform: translate(-50%, -50%) scale(.4);
  transition: opacity 150ms ease, transform 200ms var(--ease-out);
}
.page-play .menu-item:focus-visible { outline-offset: 6px; }
.page-play .menu-item:focus-visible::before, .page-play .menu-item[aria-current]::before {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
@media (hover: hover) and (pointer: fine) {
  .page-play .menu-item:hover::before { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.page-play .menu-toggle .menu-state { margin-left: .6em; color: #a3a3a6; font-size: .8em; }
.page-play .menu-toggle .menu-state::before { content: "Off"; }
.page-play .menu-toggle[aria-pressed="true"] .menu-state::before { content: "On"; }
/* Party is root's Easter egg. The gold mark follows its real state only: dim while the
   music and dance load, full while they play. Root animates the rest. */
.page-play #lobby-party:is([aria-pressed="true"], [data-state="loading"])::before { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.page-play #lobby-party[data-state="loading"]::before { opacity: .5; }

/* ---------- Character name ----------
   Native at 648px: Montserrat Black with 53px caps, right-aligned 27px from the party
   rail, then one Medium line with 18px caps whose baseline sits 59px above the floor.
   The name stays empty until the scene names the character. */
.page-play .lobby-hero {
  position: absolute;
  right: calc(var(--rail-w) + var(--u) * 4.2);
  bottom: calc(var(--u) * 8.35);
  max-width: calc(100% - var(--rail-w) - var(--u) * 60);
  text-align: right;
  color: var(--white);
  text-shadow: 0 3px 16px rgba(0, 0, 0, .55);
}
.page-play .lobby-name {
  font: 900 max(40px, calc(var(--u) * 11.7))/.9 var(--lobby);
  letter-spacing: .01em;
  text-transform: uppercase;
}
.page-play .lobby-name:empty { display: none; }
.page-play .lobby-status {
  margin-top: calc(var(--u) * 1.75);
  color: #e2e2e2;
  font: 500 max(15px, calc(var(--u) * 3.9))/1.15 var(--lobby);
}

/* ---------- Party rail ----------
   The native social panel, kept to real web actions: no friends, accounts or counts.
   It runs the full height, as in the game; the site bar ends at its left edge. */
.page-play .rail {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  width: var(--rail-w);
  min-height: 0;
  padding-right: env(safe-area-inset-right);
  background: var(--teal-0);
  box-shadow: -1px 0 0 rgba(255, 255, 255, .04);
  color: var(--white);
  overflow-y: auto;
  scrollbar-width: thin;
}
.page-play .rail-keys {
  display: flex;
  justify-content: flex-end;
  gap: 2px;
  min-height: max(48px, calc(var(--u) * 7.7));
  padding: max(2px, env(safe-area-inset-top)) 5px 2px;
  align-items: center;
}
.page-play .key {
  display: grid;
  place-items: center;
  width: 48px;
  height: 44px;
  padding: 0;
  border: 0;
  color: #8d9ba5;
  background: none;
  cursor: pointer;
  transition: color 150ms ease, transform 120ms var(--ease-out);
}
.page-play .key:active { transform: scale(.94); }
.page-play .key[aria-pressed="true"] { color: #fff; }
.page-play .motion-btn .i-play, .page-play .motion-btn[aria-pressed="true"] .i-pause { display: none; }
.page-play .motion-btn[aria-pressed="true"] .i-play { display: block; }
.page-play .sound-btn .i-sound, .page-play .sound-btn[aria-pressed="true"] .i-mute { display: none; }
.page-play .sound-btn[aria-pressed="true"] .i-sound { display: block; }
.page-play .rail-invite {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  min-height: max(56px, calc(var(--u) * 9.3));
  margin: 0;
  border: 1px solid #2c9a70;
  color: #fff;
  background: var(--green);
  font: 700 max(14px, calc(var(--u) * 2.25))/1 var(--lobby);
  letter-spacing: .02em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 150ms ease;
}
.page-play .rail-invite svg { flex: none; color: #c9eedd; }
.page-play .rail-party {
  padding: 12px 10px 10px;
  background: linear-gradient(180deg, rgba(0, 0, 0, .28), rgba(0, 0, 0, 0) 40%), var(--teal-1);
  border-bottom: 1px solid rgba(120, 160, 180, .14);
}
.page-play .rail-title, .page-play .rail-head { font: 700 15px/1.25 var(--lobby); }
.page-play .rail-sub { display: block; color: #8394a0; font: 500 12px/1.3 var(--lobby); }
.page-play .rail-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
  padding: 8px 12px;
}
.page-play .rail-head::before {
  content: "";
  border-top: 6px solid currentColor;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
}
.page-play .rail-list { margin: 0; padding: 0 6px 12px; list-style: none; flex: 1; }
.page-play .rail-list a {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 50px;
  padding: 4px 6px;
  color: var(--white);
  text-decoration: none;
  transition: background-color 150ms ease;
}
.page-play .rail-step {
  display: grid;
  flex: none;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid rgba(255, 255, 255, .12);
  background: #0e1820;
  color: #c8d3da;
  font: 700 16px/1 var(--lobby);
}
.page-play .rail-text { min-width: 0; }
.page-play .rail-name { display: block; font: 700 14px/1.25 var(--lobby); }
.page-play .rail-version {
  padding: 12px;
  background: var(--teal-2);
  color: #e9edf0;
  font: italic 700 12px/1.3 var(--lobby);
  text-align: center;
}
@media (hover: hover) and (pointer: fine) {
  .page-play .key:hover { color: #fff; }
  .page-play .rail-invite:hover { background-color: #0e7f55; }
  .page-play .rail-list a:hover { background: rgba(255, 255, 255, .05); }
}

/* ---------- Panels ----------
   Each panel is a fragment target (#how, #join, #host, #install, #faq), so it opens with
   no script and closes by going back to #main. /join keeps Join open through
   data-route. Root may also set data-open on a panel. Like the native window, the shade
   covers everything, the site bar and the loading screen included (the bar is inert
   while a panel is open), so an invite or Download opens before the room has loaded. */
body.page-play:has(.panel:is(:target, [data-open])) .topbar { z-index: 0; }
.page-play .panel {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  padding: max(clamp(12px, 8.3vh, 54px), env(safe-area-inset-top)) var(--gutter) max(clamp(12px, 8.2vh, 53px), env(safe-area-inset-bottom));
  background: rgba(0, 0, 0, .78);
  visibility: hidden;
  opacity: 0;
  transition: opacity 200ms ease, visibility 0s linear 200ms;
}
.page-play .panel:is(:target, :has(:target), [data-open], [data-route="join"]) {
  visibility: visible;
  opacity: 1;
  transition: opacity 200ms ease, visibility 0s;
}
body.page-play:has(.panel:target) .panel[data-route="join"]:not(:target),
.page-play #main:target .panel[data-route="join"]:not([data-open]) {
  visibility: hidden;
  opacity: 0;
}
.page-play .panel-box {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(980px, 100%);
  max-height: 100%;
  min-height: 0;
  border: 1px solid var(--panel-red);
  background: rgba(17, 7, 7, .95);
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, .9);
  color: #d4d0ce;
  font: 400 15px/1.55 var(--lobby);
  transform: translateY(10px) scale(.985);
  transition: transform 220ms var(--ease-out);
}
/* Brighter red corners, as on the native window. */
.page-play .panel-box::before {
  content: "";
  position: absolute;
  inset: -1px;
  pointer-events: none;
  --c: var(--panel-hot);
  background:
    linear-gradient(var(--c), var(--c)) top left / 16px 2px,
    linear-gradient(var(--c), var(--c)) top left / 2px 16px,
    linear-gradient(var(--c), var(--c)) top right / 16px 2px,
    linear-gradient(var(--c), var(--c)) top right / 2px 16px,
    linear-gradient(var(--c), var(--c)) bottom left / 16px 2px,
    linear-gradient(var(--c), var(--c)) bottom left / 2px 16px,
    linear-gradient(var(--c), var(--c)) bottom right / 16px 2px,
    linear-gradient(var(--c), var(--c)) bottom right / 2px 16px;
  background-repeat: no-repeat;
}
.page-play .panel:is(:target, :has(:target), [data-open], [data-route="join"]) .panel-box { transform: none; }
.page-play .panel-head {
  position: relative;
  flex: none;
  margin: 0 20px;
  padding: 16px 52px 12px;
  border-bottom: 1px solid rgba(255, 255, 255, .12);
  text-align: center;
}
.page-play .panel h2 {
  color: #fff;
  font: 400 clamp(17px, 2.9vh, 22px)/1.2 var(--lobby-small);
  letter-spacing: .2em;
  text-transform: uppercase;
}
.page-play .panel-sub { margin-top: 4px; color: #c4c0bf; font: 400 12px/1.4 var(--lobby-small); letter-spacing: .03em; }
.page-play .panel-close {
  position: absolute;
  top: 6px;
  right: -12px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  color: #a7a3a2;
  transition: color 150ms ease;
}
.page-play .panel-body {
  min-height: 0;
  padding: 20px 40px 28px;
  overflow: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: #4a2a2a transparent;
}
.page-play .panel-split {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: 20px 40px;
  align-items: start;
}
.page-play .panel-body p + p { margin-top: 10px; }
.page-play .panel a:not(.cta):not(.screen):not(.tile):not(.panel-close) {
  color: #fff;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(255, 255, 255, .35);
}
.page-play .muted { color: #8f8a88; }
.page-play .aside-label, .page-play .field-label, .page-play .verify-label {
  display: block;
  margin-bottom: 8px;
  color: #a3a3a6;
  font: 400 11px/1.3 var(--lobby-small);
  letter-spacing: .14em;
  text-transform: uppercase;
}
@media (hover: hover) and (pointer: fine) {
  .page-play .panel-close:hover { color: #fff; }
}

/* Play chooser: square map tiles with a label bar. Hover and focus take the native
   selected look (red frame, red bar). */
.page-play .tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.page-play .tile {
  display: flex;
  flex-direction: column;
  border: 1px solid #3c3a3a;
  background: #0b0b0b;
  color: #dcdcdc;
  text-decoration: none;
  transition: border-color 150ms ease, transform 120ms var(--ease-out);
}
.page-play .tile img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
.page-play .tile-label {
  padding: 8px 12px 2px;
  border-top: 1px solid #2c2a2a;
  font: 400 15px/1.3 var(--lobby-small);
  letter-spacing: .2em;
  text-align: center;
  text-transform: uppercase;
  transition: background-color 150ms ease, color 150ms ease;
}
.page-play .tile-note { padding: 0 12px 10px; color: #9b9796; font: 400 12px/1.55 var(--lobby-small); text-align: center; transition: color 150ms ease; }
.page-play .tile:active { transform: scale(.985); }
.page-play .tile:focus-visible { outline-offset: 2px; }
.page-play .tile:focus-visible, .page-play .tile:focus-visible :is(.tile-label, .tile-note) { border-color: #c01414; background: var(--pick); color: #fff; }
@media (hover: hover) and (pointer: fine) {
  .page-play .tile:hover { border-color: #c01414; }
  .page-play .tile:hover :is(.tile-label, .tile-note) { background: var(--pick); color: #fff; }
}
.page-play .panel-foot { display: flex; justify-content: center; margin-top: 20px; }

/* ---------- Controls ----------
   Primary: the native selected red. Quiet: the native gray Start Match key. Square. */
.page-play .cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  max-width: 100%;
  padding: 0 28px;
  border: 1px solid #c4161a;
  color: #fff;
  background: #9b0b0b;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14);
  font: 700 14px/1.2 var(--lobby);
  letter-spacing: .16em;
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease, transform 120ms var(--ease-out);
}
.page-play .cta:active { transform: scale(.97); }
.page-play .cta-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.page-play .cta-icon { flex: none; }
.page-play .cta-quiet {
  border-color: rgba(255, 255, 255, .22);
  color: #f2f2f2;
  background: linear-gradient(180deg, #4f4f51, #3a3a3c);
}
.page-play .cta-big { min-height: 56px; padding: 0 30px; font-size: 15px; }
.page-play .cta-small { min-height: 44px; padding: 0 18px; font-size: 12px; }
.page-play .cta-full { width: 100%; }
.page-play .cta[aria-disabled="true"] {
  border-color: rgba(255, 255, 255, .08);
  color: #807876;
  background: #1a1716;
  box-shadow: none;
  cursor: default;
}
.page-play .cta[aria-disabled="true"]:active { transform: none; }
@media (hover: hover) and (pointer: fine) {
  .page-play .cta:not(.cta-quiet):not([aria-disabled="true"]):hover { background-color: #b80f0f; }
  .page-play .cta-quiet:hover { border-color: rgba(255, 255, 255, .42); }
}

/* Numbered steps in square counters. */
.page-play .steps { margin: 0; padding: 0; list-style: none; counter-reset: step; }
.page-play .steps li {
  position: relative;
  min-height: 30px;
  padding-left: 44px;
  counter-increment: step;
}
.page-play .steps li + li { margin-top: 12px; }
.page-play .steps li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: -2px;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid rgba(255, 255, 255, .16);
  background: #1b1212;
  color: #fff;
  font: 700 13px/1 var(--lobby);
}

/* Screenshots open in the viewer (app.mjs). */
.page-play .screen { display: block; border: 1px solid #3c3a3a; line-height: 0; transition: border-color 150ms ease; }
.page-play .screen img { width: 100%; height: auto; }
.page-play .screen-note { margin-top: 8px; color: #9b9796; font-size: 13px; }
@media (hover: hover) and (pointer: fine) { .page-play .screen:hover { border-color: #c01414; } }

/* Host: the five maps, shown as in the native picker but not selectable here. */
.page-play .maps {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}
.page-play .maps li { border: 1px solid #3c3a3a; background: #0b0b0b; }
.page-play .maps img { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover; }
.page-play .maps span {
  display: block;
  padding: 5px 4px;
  border-top: 1px solid #2c2a2a;
  color: #cfcfcf;
  font: 400 11px/1.55 var(--lobby-small);
  letter-spacing: .16em;
  text-align: center;
  text-transform: uppercase;
}
.page-play .notes { margin-top: 20px; color: #b8b3b1; font-size: 14px; }

/* Join form. Visual state follows #join[data-state]. */
.page-play .input-row { display: flex; gap: 8px; }
.page-play input {
  flex: 1;
  min-width: 0;
  height: 48px;
  padding: 0 14px;
  border: 1px solid #3f3c3c;
  border-radius: 0;
  color: #fff;
  background: #0a0909;
  font: 400 16px/1 var(--lobby);
  caret-color: var(--panel-hot);
  transition: border-color 150ms ease;
}
.page-play input::placeholder { color: #7a7473; opacity: 1; }
.page-play input:focus { outline: none; border-color: var(--panel-hot); }
.page-play input:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.page-play input[aria-invalid="true"] { border-color: var(--bad); }
.page-play #join[data-state="valid"] input { border-color: #2c9a70; }
.page-play .clear, .page-play .copy {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 44px;
  height: 48px;
  padding: 0 14px;
  border: 1px solid rgba(255, 255, 255, .2);
  border-radius: 0;
  color: #eee;
  background: linear-gradient(180deg, #3f3f41, #2f2f31);
  font: 700 12px/1 var(--lobby);
  letter-spacing: .12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 150ms ease, transform 120ms var(--ease-out);
}
.page-play .clear:active, .page-play .copy:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) { .page-play .clear:hover, .page-play .copy:hover { border-color: rgba(255, 255, 255, .42); } }
.page-play .session-info {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
  padding: 8px;
  border: 1px solid #c01414;
  background: rgba(163, 0, 0, .18);
}
.page-play .map-art {
  flex: none;
  width: 112px;
  aspect-ratio: 16 / 9;
  background: #151313 center / cover no-repeat;
}
.page-play .map-art[data-map="asylum"] { background-image: url("/site-20261023-cleanup/art/asylum-480.jpg"); }
.page-play .map-art[data-map="arctic"] { background-image: url("/site-20261023-cleanup/art/arctic-480.jpg"); }
.page-play .map-art[data-map="forest"] { background-image: url("/site-20261023-cleanup/art/forest-480.jpg"); }
.page-play .map-art[data-map="lumberyard"] { background-image: url("/site-20261023-cleanup/art/lumberyard-480.jpg"); }
.page-play .map-art[data-map="mansion"] { background-image: url("/site-20261023-cleanup/art/manor-480.jpg"); }
.page-play .map-name { color: #fff; font-size: 15px; letter-spacing: .16em; text-transform: uppercase; }
.page-play .map-version { color: #b8b3b1; font-size: 13px; }
.page-play .version-note { margin-top: 12px; padding: 10px 12px; border-left: 2px solid var(--gold); background: rgba(214, 173, 87, .08); color: #e8dcc0; font-size: 14px; }
.page-play .feedback { min-height: 3.1em; margin: 12px 0 14px; color: #a9a4a2; font-size: 14px; }
.page-play .feedback.error { color: var(--bad); }
.page-play .fallback { margin-top: 14px; color: #b8b3b1; font-size: 14px; }

/* Download ticket. app.mjs owns data-state, the label, the details and Try again. */
.page-play .install-copy { color: #c9c4c2; }
.page-play .needs { color: #9b9796; font-size: 14px; }
.page-play .ticket { display: flex; flex-direction: column; align-items: stretch; gap: 14px; }
.page-play .ticket .cta-big { width: 100%; }
.page-play .ticket-build { color: #fff; font-size: 14px; letter-spacing: .06em; }
.page-play .download-detail { color: #a9a4a2; font-size: 13px; }
.page-play .ticket[data-state="loading"] .ticket-build { color: #a9a4a2; }
.page-play .ticket[data-state="off"] .ticket-build { color: var(--bad); }
.page-play .ticket #download-retry { align-self: flex-start; }
.page-play .install-more { font-size: 13px; }
/* Phones: the full label wraps between words instead of ending in an ellipsis. */
@media (max-width: 480px) {
  .page-play .ticket .cta-big {
    padding: 10px 16px;
    font-size: 14px;
    line-height: 1.25;
    letter-spacing: .1em;
    white-space: normal;
    text-align: center;
  }
  .page-play .ticket .cta-label { overflow: visible; text-overflow: clip; }
}

/* Help: plain rows with a plus that turns, no cards. The window hangs from a fixed top,
   so a row never moves under the pointer: an answer pushes only the rows below it, and
   past the screen's height the body scrolls inside the frame. The gutter keeps the text
   width when the scrollbar appears, so a measured answer height stays true.
   Root animates an answer while the row has data-animating="open" or "close": the answer
   clips meanwhile, and the plus shows the target state at once. Its bottom space is a
   spacer, not padding, so an answer can start from a true zero height. */
.page-play .panel-help { align-items: start; }
.page-play .panel-help .panel-body { scrollbar-gutter: stable both-edges; }
.page-play .help-list { border-top: 1px solid rgba(255, 255, 255, .1); }
.page-play .qa { border-bottom: 1px solid rgba(255, 255, 255, .1); }
.page-play .qa summary {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 52px;
  padding: 8px 40px 8px 0;
  color: #fff;
  font: 700 15px/1.3 var(--lobby);
  letter-spacing: .02em;
  list-style: none;
  cursor: pointer;
}
.page-play .qa summary::-webkit-details-marker { display: none; }
.page-play .qa summary::before, .page-play .qa summary::after {
  content: "";
  position: absolute;
  right: 12px;
  top: 50%;
  width: 12px;
  height: 2px;
  background: #a7a3a2;
  transition: transform 200ms var(--ease-out);
}
.page-play .qa summary::after { transform: rotate(90deg); }
.page-play .qa[open] summary::after { transform: rotate(0deg); }
.page-play .qa[data-animating="close"] summary::after { transform: rotate(90deg); }
.page-play .qa-body { color: #c4bfbd; font-size: 14px; }
.page-play .qa-body::after { content: ""; display: block; height: 18px; }
.page-play .qa[data-animating] > .qa-body { overflow: hidden; }
.page-play .qa-body .screen { margin-top: 14px; }
.page-play .facts { display: flex; flex-wrap: wrap; gap: 8px 28px; margin: 0 0 12px; }
.page-play .facts dt { color: #a3a3a6; font: 400 11px/1.55 var(--lobby-small); letter-spacing: .14em; text-transform: uppercase; }
.page-play .facts dd { margin: 0; color: #fff; }
.page-play .release-note { margin-bottom: 12px; }
.page-play .sha-row { display: flex; gap: 8px; align-items: stretch; }
.page-play .sha-row code {
  flex: 1;
  min-width: 0;
  padding: 12px;
  border: 1px solid #3f3c3c;
  background: #0a0909;
  color: #e6e1df;
  font: 400 12px/1.5 var(--mono);
  overflow-wrap: anywhere;
}
.page-play .copy .copy-tick { stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; transition: stroke-dashoffset 200ms var(--ease-out), opacity 100ms ease; }
.page-play .copy .copy-pages { transition: opacity 100ms ease; }
.page-play .copy[data-done] .copy-tick { stroke-dashoffset: 0; opacity: 1; }
.page-play .copy[data-done] .copy-pages { opacity: 0; }
.page-play .copy-status { min-height: 1.5em; margin: 6px 0 8px; color: var(--ok); font-size: 13px; }

/* Full-size screenshot viewer (app.mjs builds it). */
.page-play .lightbox {
  max-width: min(96vw, 1400px);
  max-height: 94vh;
  padding: 0;
  border: 1px solid var(--panel-red);
  background: #070606;
  color: #fff;
}
.page-play .lightbox::backdrop { background: rgba(0, 0, 0, .86); }
.page-play .lightbox-bar { display: flex; justify-content: flex-end; gap: 6px; padding: 6px; }
.page-play .lightbox .btn {
  min-width: 44px;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid rgba(255, 255, 255, .2);
  color: #eee;
  background: linear-gradient(180deg, #3f3f41, #2f2f31);
  font: 700 12px/1 var(--lobby);
  letter-spacing: .12em;
  text-transform: uppercase;
}
.page-play .lightbox-img { display: block; width: auto; max-width: 100%; height: auto; max-height: calc(94vh - 58px); margin: 0 auto; }

/* ---------- Loading screen ----------
   Root sets body[data-loading] to "true" or "false". The page ships "true", so the first
   paint is this screen. It covers the room, the menu and the rail, but stays under the
   header and an open panel, so Download, Join and Help keep working while the scene
   loads. The menu and rail hide under it, so keyboard focus never lands on a covered
   control. It fades out as soon as root clears the flag, with no hold.
   Not scoped to .page-play: root also shows it while Equipment loads.
   The ring is the game's own loading animation (UI.pak deceit_ui_loading_000 to 048:
   49 frames of 128px in a 7 by 7 sheet). It steps with transforms, which stay smooth while
   the scene keeps the main thread busy, and it freezes in place once loading ends. */
.lobby-loading {
  --u: min(1vh, .6vw);
  --ring: 64px;
  position: fixed;
  inset: 0;
  z-index: 30;
  display: grid;
  place-items: center;
  padding: var(--shell-h) var(--gutter);
  background: #050506;
  color: #f5f5f5;
  text-align: center;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 250ms var(--ease-out), visibility 0s linear 250ms;
}
body[data-loading="true"] .lobby-loading {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transition: opacity 250ms var(--ease-out), visibility 0s;
}
body[data-loading="true"] #main :is(.lobby-left, .rail) { visibility: hidden; }
.lobby-loading-mark { display: grid; justify-items: center; }
/* The native title face (LOADOUT): Montserrat Black, widely tracked. The left padding
   balances the tracking after the last letter. */
.lobby-loading-word {
  padding-left: .24em;
  font: 900 clamp(34px, calc(var(--u) * 7.4), 88px)/1 var(--lobby);
  letter-spacing: .24em;
}
.lobby-loading-ring {
  position: relative;
  width: var(--ring);
  height: var(--ring);
  margin-top: max(18px, calc(var(--u) * 3.6));
  overflow: hidden;
}
.lobby-loading-ring::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: calc(var(--ring) * 7);
  height: calc(var(--ring) * 7);
  background: url("/lobby/loading.webp") 0 0 / 100% 100% no-repeat;
  animation: lobby-loading-col 240ms steps(7) infinite paused, lobby-loading-row 1680ms steps(7) infinite paused;
}
body[data-loading="true"] .lobby-loading-ring::before { animation-play-state: running; }
/* Seven columns per row, seven rows per loop: 49 frames at about 29 per second. */
@keyframes lobby-loading-col { to { translate: -100% 0; } }
@keyframes lobby-loading-row { to { transform: translateY(-100%); } }
@keyframes lobby-loading-breathe { from { opacity: .35; } }
.lobby-loading-label {
  margin-top: max(8px, calc(var(--u) * 1.4));
  color: #a6a6a9;
  font: 400 max(11px, calc(var(--u) * 1.6))/1.3 var(--lobby-small);
  letter-spacing: .2em;
  text-transform: uppercase;
}
/* Whole-pixel sizes keep each frame's edge clean: 128px frames at a half or three quarters. */
@media (min-width: 1280px) and (min-height: 800px) {
  .lobby-loading { --ring: 96px; }
}

/* ---------- Party ----------
   Root draws the confetti here and drives the dance with WAAPI. The layer never takes a
   click. It falls over the header and stays under an open panel. */
.party-confetti {
  position: fixed;
  inset: 0;
  z-index: 55;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ---------- Equipment view ----------
   Root mounts the Skins app in #app and switches body.page-play to body.page-equipment.
   The view fills the screen below the header. It keeps the app's margins inside
   (flow-root) and its layers to itself (isolate), so the header stays above everything
   in it. While Play is still the page, root mounts it behind the loading screen; it then
   sits out of the flow at the same size, so its first frame already has its final
   framing and Play gets no scrollbar. */
#equipment-view {
  display: flow-root;
  position: relative;
  isolation: isolate;
  min-height: calc(100vh - var(--shell-h));
  min-height: calc(100dvh - var(--shell-h));
}
.page-play #equipment-view { position: fixed; inset: var(--shell-h) 0 0; }

/* ---------- Widths ----------
   Below 1100px the rail would crowd the character, so it goes; its actions remain in
   the menu and the header. */
@media (min-width: 1100px) {
  .page-play .topbar { right: var(--rail-w); }
}
@media (max-width: 1099px) {
  .page-play .rail { display: none; }
  .page-play .lobby-hero { right: max(var(--gutter), calc(var(--u) * 4.2)); max-width: calc(100% - var(--u) * 60); }
}

/* Phones and portrait tablets: the name sits at the top right and the menu at the bottom
   left, both in the page flow, so a tall menu makes the page scroll instead of running
   under the site bar. Panels fill the screen. */
@media (max-width: 760px) {
  .page-play { --u: 1vh; }
  .page-play .lobby {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    min-height: 100vh;
    min-height: 100dvh;
    padding: calc(var(--shell-h) + 16px) var(--gutter) max(20px, env(safe-area-inset-bottom));
  }
  .page-play .lobby-hero {
    position: static;
    order: -1;
    align-self: flex-end;
    max-width: 100%;
    margin-bottom: auto;
  }
  .page-play .lobby-left { position: static; max-width: 100%; margin-top: 24px; padding-left: 22px; }
  .page-play .menu-big { min-height: 50px; font-size: clamp(30px, 9vw, 40px); }
  .page-play .menu-small { min-height: 44px; font-size: 18px; }
  .page-play .menu-item::before { left: -14px; }
  .page-play .lobby-name { font-size: clamp(40px, 13vw, 56px); }
  .page-play .lobby-status { margin-top: 6px; font-size: 15px; }

  .page-play .panel { place-items: stretch; padding: 0; }
  .page-play .panel-box { width: 100%; height: 100%; border-width: 0; }
  .page-play .panel-box::before { display: none; }
  .page-play .panel-head { margin: 0 var(--gutter); padding: max(14px, env(safe-area-inset-top)) 44px 10px; }
  .page-play .panel-close { right: -14px; top: max(4px, calc(env(safe-area-inset-top) - 10px)); }
  .page-play .panel-body { padding: 16px var(--gutter) max(28px, env(safe-area-inset-bottom)); }
  .page-play .panel-split { grid-template-columns: minmax(0, 1fr); }
  .page-play .tiles { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .page-play .maps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .page-play .panel-foot .cta { width: 100%; }
  .page-play .session-info { flex-wrap: wrap; }
}
@media (max-width: 380px) {
  .page-play .cta { padding: 0 14px; letter-spacing: .08em; white-space: normal; text-align: center; }
  .page-play .cta-big { font-size: 13px; }
  .page-play .cta-label { overflow: visible; }
  .page-play .input-row { flex-wrap: wrap; }
  .page-play .input-row .clear { width: 100%; justify-content: center; }
  .page-play .maps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Landscape phones: keep the menu on the left of the name, both on the floor. The page
   scrolls when the menu is taller than the screen. */
@media (max-height: 480px) and (min-width: 561px) {
  .page-play { --u: 1vh; }
  .page-play .menu-big { min-height: 44px; font-size: 28px; }
  .page-play .menu-small { min-height: 44px; font-size: 16px; }
  .page-play .lobby-name { font-size: 40px; }
  .page-play .lobby-status { font-size: 15px; }
}
@media (max-height: 480px) and (min-width: 561px) and (max-width: 760px) {
  .page-play .lobby { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: 24px; }
  .page-play .lobby-hero { order: 0; align-self: flex-end; margin: 0; }
  .page-play .lobby-left { margin-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .page-play *, .page-play *::before, .page-play *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
  }
  .page-play .panel-box { transform: none; transition: none; }
  .page-play .tile, .page-play .cta, .page-play .key, .page-play .clear, .page-play .copy, .page-play .menu-item::before { transition: none; }
  .page-play .tile:active, .page-play .cta:active, .page-play .key:active { transform: none; }
  /* The loading ring stops turning. One full frame breathes instead, so a slow load still
     looks alive without movement. */
  .lobby-loading .lobby-loading-ring::before {
    translate: calc(-300% / 7) 0;
    transform: translateY(calc(-300% / 7));
    animation: none !important;
  }
  body[data-loading="true"] .lobby-loading .lobby-loading-ring {
    animation: lobby-loading-breathe 1.2s ease-in-out infinite alternate !important;
  }
}

/* Without JavaScript nothing clears the loading flag or runs Party: the lobby shows over
   the room still instead. */
@media (scripting: none) {
  .lobby-loading, #lobby-party { display: none; }
  body[data-loading] #main :is(.lobby-left, .rail) { visibility: visible; }
  .page-play .lobby-room-still { opacity: 1; }
}
