/* Llama Party controller theme — the big screen's look on a phone: near-black
   glass, paper cream for what you read or type into (How to play, a
   focused field), gold for the main action (Join, Pause game), selection
   outlined in cream.
   The player's own colour (--me) marks what is theirs: the stick knob and
   the A button. Thumb-reach + instant feedback over density; text inputs
   live in the upper half (the Jackbox pattern). */

/* tokens.css and fonts.css load beside this sheet (index.html). */

* {
  box-sizing: border-box;
}

html,
body,
#app {
  width: 100%;
  height: 100%;
  margin: 0;
}

html {
  /* The viewport takes overscroll from the ROOT element only — the body copy
     below never reached it (spec; Chrome 93+). Kills pull-to-refresh, which
     mid-game is a disconnect. */
  overscroll-behavior: none;
  /* iOS Safari inflates text it deems too small after a rotation — and this
     app rotates every session (portrait C5 blocker → landscape pad). Sizes
     are ours alone. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* What an iPhone shows past the page's ends — the keyboard lifting the
     page, or Chrome there shrinking it — is the root's colour, and the
     body's gradient gives none: it was white, half the screen of it while
     typing on the join page. Dark, so the keyboard and the browser's own
     bits are dark too. */
  background-color: var(--surface-well);
  color-scheme: dark;
}

body {
  background: linear-gradient(160deg, var(--surface-raised), var(--surface-well));
  color: var(--cream);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
  /* pan-x pan-y, NOT manipulation — manipulation still offers the browser
     pinch-zoom, and iOS ignores user-scalable=no, so two-thumb play kept
     zooming the pad. Pans stay listed so the C1 join card can scroll;
     install_gesture_guards (app/browser.rs) is the belt over these braces. */
  touch-action: pan-x pan-y;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

button {
  font-family: inherit;
  border: none;
  cursor: pointer;
  color: inherit;
  background: none;
  padding: 0;
}

/* Canon Button disabled grammar (controls/Button). */
button:disabled {
  opacity: 0.42;
  filter: grayscale(0.3);
  pointer-events: none;
}

input {
  font-family: inherit;
}

/* Decorative art (mascot, wordmark) is UI, not content — it must never lift
   as a drag ghost or read as saveable/selectable media. */
img {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
}

/* ---------------------------------------------------------------- */
/* C1 · Pair / join — pairing with a name IS joining           */

/* One column: the form at the very top, the wordmark at the bottom. A
   phone keyboard covers the bottom 40–45% of an upright screen, so the
   form sits well above it and nothing moves when it comes up (a field near
   the keyboard's edge made iOS slide the whole page up); it covers the
   wordmark. The layout follows how the phone is held (`data-shape` on the
   root, from the screen: app/browser.rs), never the page's shape, which a
   keyboard changes on some browsers (Chrome on an iPhone). Nor its height:
   while a field is typed in, the stage keeps the height it had before
   (`--stage-height`, app/browser.rs) as such a browser shrinks the page, so
   the wordmark stays at the bottom for the keyboard to slide over. It paints
   the body's gradient itself, so past where the shrunk body ends the page
   looks the same. */
.c1-stage {
  position: fixed;
  inset: 0;
  height: var(--stage-height, auto);
  background: linear-gradient(160deg, var(--surface-raised), var(--surface-well));
  display: grid;
  grid-template-columns: min(440px, 100%);
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas: "form" "art";
  justify-content: center;
  row-gap: 14px;
  padding: max(14px, env(safe-area-inset-top)) max(16px, env(safe-area-inset-right)) 0 max(16px, env(safe-area-inset-left));
  /* Clipped, not a scroller: a focused field would scroll a hidden one,
     lifting the form off the top. */
  overflow: hidden;
  overflow: clip;
  overscroll-behavior: contain;
}

.c1-brand {
  display: flex;
  justify-content: center;
  height: 64px;
}

.c1-wordmark {
  height: 100%;
  width: auto;
}

/* Glass without the glass's blur: behind the form is only the stage's
   smooth gradient, which a blur leaves as it is (the shade it took off is a
   level or so of 255), and redoing it for every letter typed cost a phone
   a frame. */
.c1-form {
  grid-area: form;
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--glass);
  border-radius: 24px;
  /* The error under it lines up with the fields (.c1-error). */
  --form-pad: 16px;
  padding: var(--form-pad);
  box-shadow: var(--shadow-float);
}

/* The error hangs under the form (.c1-error), so the fields keep their gaps
   with one or without. */
.c1-field.is-name {
  margin-top: 14px;
}

.c1-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 0 2px 6px;
  font-size: 14px;
  font-weight: var(--w-heavy);
  color: var(--text-dim);
}

.c1-label-hint {
  font-size: 12.5px;
  font-weight: var(--w-bold);
  opacity: 0.85;
}

/* The room code: one box, its four letters spaced out in it, its field
   lying unseen over it. Four boxes read as four fields to tap one by one
   (people tap each part of a split field); one reads as one, the way the
   name's does, and the letter slots still say it's four. */
.c1-slots {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, 56px);
  justify-content: center;
  border-radius: 16px;
  background: var(--quiet);
  transition: background var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}

.c1-slot {
  position: relative;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 34px;
  color: var(--cream);
  pointer-events: none;
  transition: color var(--dur-fast) var(--ease-out);
}

/* A dash where each letter still to come goes. */
.c1-slot:empty::before {
  content: "";
  width: 22px;
  height: 3px;
  margin-top: 22px;
  border-radius: 2px;
  background: rgba(194, 187, 175, 0.32);
}

/* Typing: the box cream (cream is where you are), as the name's goes, a
   caret at the next letter. */
.c1-slots:focus-within {
  background: var(--paper);
  box-shadow: 0 0 0 6px rgba(255, 242, 216, 0.16);
}

.c1-slots:focus-within .c1-slot {
  color: var(--ink);
}

.c1-slots:focus-within .c1-slot:empty::before {
  background: var(--paper-text2);
  opacity: 0.45;
}

/* The caret stands in for the next letter's dash. */
.c1-slots:focus-within .c1-slot.is-next:empty::before {
  display: none;
}

.c1-slots:focus-within .c1-slot.is-next::after {
  content: "";
  position: absolute;
  width: 3px;
  height: 34px;
  border-radius: 2px;
  background: var(--ink);
  animation: c1-caret 1.1s steps(1) infinite;
}

@keyframes c1-caret {
  50% {
    opacity: 0;
  }
}

/* Letters selected in the field (to type over): highlighted in their
   slots, the caret away as a field's is while something's selected. */
.c1-slots:focus-within .c1-slot.is-selected {
  background: linear-gradient(rgba(255, 200, 87, 0.6), rgba(255, 200, 87, 0.6)) center / 100% 44px no-repeat;
}

.c1-slots.has-selection:focus-within .c1-slot.is-next::after {
  display: none;
}

.c1-slots.is-error {
  box-shadow: inset 0 0 0 2px var(--red);
}

.c1-slots.is-error:focus-within {
  box-shadow: inset 0 0 0 2px var(--red), 0 0 0 6px rgba(255, 242, 216, 0.16);
}

/* Scan (a phone with a camera to ask for): a button beside the code's box,
   as tall as it. The home-screen app's way in: a QR scanned with an
   iPhone's Camera opens Safari, not the app. */
.c1-code-line {
  display: flex;
  gap: 10px;
}

.c1-code-line .c1-slots {
  flex: 1;
  min-width: 0;
}

.c1-scan-open {
  flex: 0 0 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 16px;
  background: var(--quiet);
  color: var(--cream);
  transition: background var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}

.c1-scan-open:active {
  transform: scale(0.96);
  background: var(--quiet-hover);
}

.c1-scan-open:disabled {
  opacity: 0.4;
}

/* A narrow phone (320 wide): the letters closer, so both fit. */
@media (max-width: 359px) {
  .c1-code-line .c1-slots {
    grid-template-columns: repeat(4, 44px);
  }

  .c1-scan-open {
    flex-basis: 52px;
  }
}

/* Typing on its side, the code's row is the field alone. */
:root[data-shape="wide"].is-typing .c1-scan-open {
  display: none;
}

/* Scan's sheet: the camera's view over everything, a square to aim with
   (the room around it dimmed), the hint above, and zoom's steps and Cancel
   below. Two fingers on it zoom the camera (camera.rs), never the page. */
.c1-scan {
  position: fixed;
  inset: 0;
  z-index: 40;
  background: #000;
  overflow: hidden;
  touch-action: none;
}

/* Blown up from its middle past what the lens zooms (camera.rs). */
.c1-scan-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}

.c1-scan-frame {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(64vw, 56vh);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 24px;
  border: 3px solid var(--cream);
  box-shadow: 0 0 0 100vmax rgba(8, 9, 12, 0.5);
  pointer-events: none;
}

.c1-scan-hint {
  position: absolute;
  top: max(18px, env(safe-area-inset-top));
  left: max(16px, env(safe-area-inset-left));
  right: max(16px, env(safe-area-inset-right));
  text-align: center;
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 19px;
  line-height: 1.25;
  color: var(--cream);
  text-shadow: 0 1px 6px rgba(8, 9, 12, 0.6);
}

.c1-scan-cancel {
  position: absolute;
  left: 50%;
  bottom: max(18px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  min-width: 140px;
}

.c1-scan-cancel:active {
  transform: translateX(-50%) translateY(1px) scale(0.985);
}

/* Zoom's steps over Cancel, as the Camera's: the one reached lit, showing
   the zoom between steps as a pinch goes. */
.c1-scan-zoom {
  position: absolute;
  left: 50%;
  bottom: calc(max(18px, env(safe-area-inset-bottom)) + 60px);
  transform: translateX(-50%);
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: var(--r-pill);
  background: rgba(8, 9, 12, 0.5);
}

.c1-scan-zoom-step {
  min-width: 52px;
  height: 44px;
  padding: 0 10px;
  border-radius: var(--r-pill);
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

.c1-scan-zoom-step[aria-pressed="true"] {
  background: var(--cream);
  color: var(--ink);
}

/* On its side, the steps stand at the right, clear of the square. */
:root[data-shape="wide"] .c1-scan-zoom {
  left: auto;
  right: max(16px, env(safe-area-inset-right));
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
  flex-direction: column;
}

/* The code's own field: over the slots, takes the taps and the keyboard,
   shows nothing (its letters are the slots'). 24px: iOS zooms into a field
   under 16. */
.c1-code {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: none;
  outline: none;
  background: transparent;
  color: transparent;
  -webkit-text-fill-color: transparent;
  caret-color: transparent;
  font-size: 24px;
  text-transform: uppercase;
  user-select: text;
  -webkit-user-select: text;
}

/* Its selection too: a browser paints selected text in the selection's own
   colours, over the transparent ones (Tab or a double-click drew "GGFH" in
   white on blue over the slots). The slots show what's selected instead. */
.c1-code::selection {
  background: transparent;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* A text field: quiet at rest, cream while you type in it, ink text. */
.c1-name {
  position: relative;
}

.c1-input {
  width: 100%;
  height: 56px;
  border: none;
  border-radius: 16px;
  background: var(--quiet);
  color: var(--cream);
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 23px;
  padding: 0 18px;
  user-select: text;
  -webkit-user-select: text;
}

.c1-input::placeholder {
  color: var(--sand);
  opacity: 0.6;
  font-family: var(--font-body);
  font-weight: var(--w-bold);
  font-size: 17px;
  text-overflow: ellipsis;
}

.c1-input:focus,
.c1-input:focus-visible {
  outline: none;
  background: var(--paper);
  color: var(--ink);
  box-shadow: 0 0 0 6px rgba(255, 242, 216, 0.16);
}

.c1-input:focus::placeholder {
  color: var(--paper-text2);
}

/* "4 / 16", while the name is typed. */
.c1-count {
  position: absolute;
  right: 16px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 13px;
  font-weight: var(--w-heavy);
  color: var(--paper-text2);
  pointer-events: none;
  display: none;
}

/* Room for the count once there's a name to count (an empty field's
   placeholder keeps the width: a narrow phone's row is short of it). */
.c1-name:focus-within .c1-input:not(:placeholder-shown) {
  padding-right: 64px;
}

.c1-name:focus-within .c1-input:not(:placeholder-shown) + .c1-count {
  display: block;
}

/* The link carried the room code: a confirmation chip, not a field. */
.c1-room {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 56px;
  padding: 0 6px 0 16px;
  border-radius: 16px;
  background: var(--quiet);
}

.c1-room-check {
  display: flex;
  color: var(--green);
}

.c1-room-code {
  flex: 1;
  min-width: 0;
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 21px;
  letter-spacing: 0.08em;
}

.c1-room-change {
  min-height: 44px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  font-weight: var(--w-heavy);
  font-size: 14px;
  color: var(--text-dim);
}

.c1-room-change:active {
  background: rgba(255, 242, 216, 0.08);
}

/* Canon Button, primary lg: layered soft shadow, press = dip (never
   brightens). Gold: the main action. */
.c1-join {
  margin-top: 16px;
  height: 58px;
  border-radius: 18px;
  background: var(--action);
  color: var(--text-on-action);
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 21px;
  box-shadow:
    var(--edge-light),
    0 2px 5px rgba(8, 9, 12, 0.16),
    0 5px 12px rgba(8, 9, 12, 0.14);
  transition:
    box-shadow var(--dur-med) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.c1-join:hover {
  background: var(--action-hover);
}

.c1-join:active {
  transform: translateY(1px) scale(0.985);
  box-shadow: var(--edge-light), 0 1px 3px rgba(8, 9, 12, 0.18);
}

.c1-join:focus-visible {
  outline: none;
  box-shadow: var(--edge-light), var(--ring-focus);
}

/* Not ready to join yet: a quiet button, not a faded gold one (which reads
   brown on near-black). */
.c1-join:disabled {
  background: var(--quiet);
  color: var(--sand);
  box-shadow: none;
  opacity: 0.7;
  filter: none;
}

/* What's wrong with the code, or why the join failed: a note hung under
   the form, over the room the wordmark leaves, so nothing in the form moves
   as one comes and goes (Join, Joining…, a refusal). As wide as the fields
   and Join above it, not the card's faint glass: wider, it stuck out past
   them on both sides. */
.c1-error {
  position: absolute;
  top: calc(100% + 10px);
  left: var(--form-pad);
  right: var(--form-pad);
  padding: 11px 16px;
  border-radius: 18px;
  background: rgba(255, 104, 79, 0.16);
  font-size: 13.5px;
  font-weight: var(--w-heavy);
  line-height: 1.35;
  color: var(--danger-text);
}

/* A linked phone's "Joining…" line before its name field. */
.c1-who-title {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 21px;
  line-height: 1.2;
  margin: 16px 2px 4px;
}

/* "Are you paul?": a line under the question, then two answers. The
   suggested one is gold; with nothing to suggest both are quiet. */
.c1-claim-copy {
  font-size: 14.5px;
  line-height: 1.4;
  color: var(--text-dim);
  margin: 0 2px 2px;
}

.c1-join.is-quiet {
  background: var(--quiet);
  color: var(--cream);
  box-shadow: none;
}

.c1-join.is-quiet:hover {
  background: rgba(255, 242, 216, 0.14);
}

.c1-join + .c1-join {
  margin-top: 10px;
}

/* Below the form: the wordmark at the bottom. A keyboard covers it. */
.c1-art {
  grid-area: art;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  padding-bottom: max(18px, env(safe-area-inset-bottom));
  container: art / size;
}

/* Which phone page this is (its build label), to set beside the game's. */
.c1-build {
  margin-top: 10px;
  font-size: 11px;
  font-weight: var(--w-medium);
  color: var(--text-dim);
  opacity: 0.75;
}

/* The wordmark stays put while typing: a keyboard slides over it. Where its
   row is too short to hold it whole (a short phone, a page that shrank
   before the stage could hold its height) it fades out (never a sliced-off
   logo), and back as the row grows. */
.c1-brand,
.c1-build {
  transition: opacity var(--dur-med) var(--ease-out), visibility 0s;
}

@container art (max-height: 96px) {
  .c1-brand,
  .c1-build {
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-med) var(--ease-out), visibility 0s var(--dur-med);
  }
}

/* A browser without container queries (iOS before 16) can't tell: it puts
   the wordmark away while typing. */
@supports not (container-type: size) {
  :root.is-typing .c1-art {
    visibility: hidden;
  }
}

/* A short phone (iPhone SE): the same, tighter, so Join clears a keyboard. */
:root[data-shape="short"] .c1-stage {
  row-gap: 10px;
  padding-top: max(10px, env(safe-area-inset-top));
}

:root[data-shape="short"] .c1-brand {
  height: 48px;
}

:root[data-shape="short"] .c1-form {
  --form-pad: 14px;
}

:root[data-shape="short"] .c1-slot {
  height: 56px;
  font-size: 30px;
}

:root[data-shape="short"] .c1-input,
:root[data-shape="short"] .c1-room {
  height: 52px;
}

:root[data-shape="short"] .c1-join {
  height: 54px;
  margin-top: 12px;
}

:root[data-shape="short"] .c1-field.is-name {
  margin-top: 10px;
}

/* A phone on its side: the wordmark beside the form. */
:root[data-shape="wide"] .c1-stage {
  grid-template-columns: 38% minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  grid-template-areas: "art form";
  justify-content: stretch;
  column-gap: 22px;
  row-gap: 6px;
  padding: max(12px, env(safe-area-inset-top)) max(20px, env(safe-area-inset-right)) 0 max(20px, env(safe-area-inset-left));
}

:root[data-shape="wide"] .c1-form {
  align-self: start;
  /* The error's box is the stage, not the form (below). */
  position: static;
}

:root[data-shape="wide"] .c1-art {
  justify-content: center;
}

/* On its side there may be no room under the form: the error takes the
   wordmark's place beside it instead, the wordmark fading while it shows,
   so still nothing moves. */
:root[data-shape="wide"] .c1-error {
  top: 50%;
  left: max(20px, env(safe-area-inset-left));
  right: auto;
  width: calc((100% - max(20px, env(safe-area-inset-left)) - max(20px, env(safe-area-inset-right))) * 0.38);
  transform: translateY(-50%);
}

:root[data-shape="wide"] .c1-form.has-error ~ .c1-art .c1-brand,
:root[data-shape="wide"] .c1-form.has-error ~ .c1-art .c1-build {
  opacity: 0;
}

:root[data-shape="wide"] .c1-slot {
  height: 56px;
  font-size: 30px;
}

:root[data-shape="wide"] .c1-input,
:root[data-shape="wide"] .c1-room {
  height: 52px;
}

:root[data-shape="wide"] .c1-join {
  height: 54px;
  margin-top: 14px;
}

:root[data-shape="wide"] .c1-field.is-name {
  margin-top: 10px;
}

/* On its side with the keyboard up, about 150px are left: one row at the
   top, code, name and Join, and the rest away until it goes. */
:root[data-shape="wide"].is-typing .c1-stage {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto;
  grid-template-areas: "form";
  padding-top: max(10px, env(safe-area-inset-top));
}

:root[data-shape="wide"].is-typing .c1-brand,
:root[data-shape="wide"].is-typing .c1-art,
:root[data-shape="wide"].is-typing .c1-label,
:root[data-shape="wide"].is-typing .c1-who-title {
  display: none;
}

:root[data-shape="wide"].is-typing .c1-form {
  flex-direction: row;
  align-items: center;
  gap: 10px;
  padding: 10px;
  border-radius: 20px;
}

:root[data-shape="wide"].is-typing .c1-field.is-name {
  margin-top: 0;
}

:root[data-shape="wide"].is-typing .c1-field.is-code {
  flex: 0 0 248px;
}

:root[data-shape="wide"].is-typing .c1-field.is-name {
  flex: 1;
  min-width: 0;
}

:root[data-shape="wide"].is-typing .c1-slots {
  grid-template-columns: repeat(4, 48px);
  border-radius: 13px;
}

:root[data-shape="wide"].is-typing .c1-slot {
  height: 50px;
  font-size: 26px;
}

:root[data-shape="wide"].is-typing .c1-slot.is-next::after {
  height: 26px;
}

:root[data-shape="wide"].is-typing .c1-input,
:root[data-shape="wide"].is-typing .c1-room {
  height: 50px;
}

:root[data-shape="wide"].is-typing .c1-join {
  flex: 0 0 180px;
  height: 50px;
  margin: 0;
  font-size: 19px;
}

/* An error, typing on its side, takes a place in the row, where the
   keyboard can't cover it: the name's while the code is being fixed (the
   name can wait), else the code's. */
:root[data-shape="wide"].is-typing .c1-error {
  position: static;
  transform: none;
  padding: 0;
  border-radius: 0;
  background: none;
  flex: 0 0 248px;
  margin: 0 4px;
  font-size: 12.5px;
  line-height: 1.25;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

:root[data-shape="wide"].is-typing .c1-field.is-code:focus-within + .c1-error {
  flex: 1 1 0;
  min-width: 0;
}

:root[data-shape="wide"].is-typing .c1-form.has-error .c1-field.is-code:focus-within ~ .c1-field.is-name,
:root[data-shape="wide"].is-typing .c1-form.has-error .c1-field.is-code:focus-within ~ .c1-join,
:root[data-shape="wide"].is-typing .c1-form.has-error .c1-field.is-code:not(:focus-within) {
  display: none;
}

/* Too little left on screen, a keyboard up or a short phone on its side
   (measured, app/browser.rs): the labels go (and the wordmark, upright)
   and the form tightens, so Join rises into view. Most phones never need
   it. */
:root.is-cramped:not([data-shape="wide"]) .c1-brand,
:root.is-cramped .c1-label {
  display: none;
}

:root.is-cramped .c1-stage {
  row-gap: 0;
  padding-top: max(8px, env(safe-area-inset-top));
}

:root.is-cramped .c1-form {
  --form-pad: 12px;
}

:root.is-cramped .c1-slot {
  height: 52px;
  font-size: 28px;
}

:root.is-cramped .c1-slots:focus-within .c1-slot.is-next::after {
  height: 28px;
}

:root.is-cramped .c1-input,
:root.is-cramped .c1-room {
  height: 50px;
}

:root.is-cramped .c1-field.is-name {
  margin-top: 10px;
}

:root.is-cramped .c1-join {
  height: 52px;
  margin-top: 10px;
}

/* On its side, typing, the one row slimmer still. */
:root[data-shape="wide"].is-typing.is-cramped .c1-stage {
  padding-top: max(4px, env(safe-area-inset-top));
}

:root[data-shape="wide"].is-typing.is-cramped .c1-form {
  padding: 6px;
}

:root[data-shape="wide"].is-typing.is-cramped .c1-slot,
:root[data-shape="wide"].is-typing.is-cramped .c1-input,
:root[data-shape="wide"].is-typing.is-cramped .c1-room,
:root[data-shape="wide"].is-typing.is-cramped .c1-join {
  height: 44px;
}

:root[data-shape="wide"].is-typing.is-cramped .c1-slot {
  font-size: 24px;
}

:root[data-shape="wide"].is-typing.is-cramped .c1-field.is-name,
:root[data-shape="wide"].is-typing.is-cramped .c1-join {
  margin-top: 0;
}

/* ---------------------------------------------------------------- */
/* C5 · Rotate blocker                                                */

.c5-stage {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: center;
  padding: 24px;
  /* Static blocker — nothing here for the browser to interpret. */
  touch-action: none;
}

/* Who you are and that you're still in, while the pad waits. */
.c5-status {
  position: absolute;
  top: max(18px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100% - 32px);
  padding: 7px 14px;
  border-radius: var(--r-pill);
  background: rgba(8, 9, 12, 0.6);
  font-weight: var(--w-heavy);
  font-size: 13px;
  white-space: nowrap;
}

.c5-status:empty {
  display: none;
}

.c5-status-dot {
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: var(--r-pill);
  background: var(--cream);
}

/* A long name gives way to "Connected", never runs out of the pill. */
.c5-status-who {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.c5-status-link {
  flex: none;
  color: var(--text-dim);
}

.c5-status-link.is-on {
  color: var(--green);
}

.c5-phone {
  width: 66px;
  height: 106px;
  border-radius: 14px;
  border: 4px solid var(--cream);
  display: grid;
  /* The home bar at the bottom edge: reads as a phone (centred, a "1"). */
  place-items: end center;
  padding-bottom: 8px;
  animation: c5-rotate 1.7s var(--ease-pop) infinite;
}

.c5-phone-body {
  width: 26px;
  height: 4px;
  border-radius: 4px;
  background: var(--cream);
}

@keyframes c5-rotate {
  0%,
  20% {
    transform: rotate(0deg);
  }
  60%,
  100% {
    transform: rotate(90deg);
  }
}

/* Reduced flash stills the loop (tokens.css): the phone rests turned. */
:root[data-reduced-flash="1"] .c5-phone {
  transform: rotate(90deg);
}

.c5-title {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 24px;
}

.c5-copy {
  font-weight: var(--w-bold);
  font-size: 13.5px;
  color: var(--text-dim);
  max-width: 34ch;
}

/* ---------------------------------------------------------------- */
/* C2 · The virtual gamepad                                    */
/*                                                                    */
/* Stick side and button side split the screen in half (mirrored for  */
/* left-handers). Each letter has one home, measured from the button   */
/* side's outer-bottom corner in thumb units (--u), the same in every  */
/* game: A where the thumb rests, B above it, X to its left, Y furthest */
/* up-left. Only declared slots are drawn; the side between them is    */
/* live (a touch goes to the nearest button — layout.rs).              */

.c2-stage {
  --u: min(1vmin, 4.6px);
  --me: var(--teal);
  --side-l: max(env(safe-area-inset-left), 14px);
  --side-r: max(env(safe-area-inset-right), 14px);
  --side-b: max(env(safe-area-inset-bottom), 6px);
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: linear-gradient(160deg, var(--surface-raised), var(--surface-well));
  /* The gamepad never pans or zooms. The stick zone and buttons already say
     so; this closes the gaps between them (stage background, bar, sheets)
     where a stray thumb could still start a browser gesture. Anything inside
     that scrolls itself would still scroll — ancestors above a scroller are
     not consulted for its pans. */
  touch-action: none;
}

.c2-stage.is-overlay {
  background: var(--ink);
}

.c2-flight-view {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* Over the view: "Loading the 3D view…" and its progress bar, or why it
   isn't up. Centred across the stage, between the stick and the buttons. */
.c2-view-status {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  row-gap: 14px;
  padding: 0 25%;
  text-align: center;
  pointer-events: none;
}

.c2-view-status .c3-copy {
  max-width: none;
}

/* The canon progress bar (hud::progress_bar): a faint cream track, the fill
   in gold. */
.c2-view-progress {
  position: relative;
  width: min(180px, 100%);
  height: 6px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--cream) 14%, transparent);
  overflow: hidden;
}

/* Slid in from the left by phone_view.rs, between pieces of the view's
   build. The glint, a compositor animation, keeps moving while a piece
   holds the page's own thread. */
.c2-view-progress-fill {
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background: var(--gold);
  transform: translateX(-100%);
  transition: transform 0.4s var(--ease-out);
  overflow: hidden;
}

.c2-view-progress-fill::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.5), transparent);
  transform: translateX(-100%);
  animation: c2-view-glint 1.4s ease-in-out infinite;
}

@keyframes c2-view-glint {
  to {
    transform: translateX(100%);
  }
}

/* Slower, not stopped: loading must stay visibly alive. (Reduced Flash's
   clamp on every element doesn't reach a pseudo-element.) */
@media (prefers-reduced-motion: reduce) {
  .c2-view-progress-fill::after {
    animation-duration: 2.8s;
  }
}

:root[data-reduced-flash="1"] .c2-view-progress-fill::after {
  animation-duration: 2.8s;
}

/* Top bar — top-centre, status and utility only. */
.c2-bar {
  position: absolute;
  top: max(8px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  z-index: 6;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: calc(100vw - 32px);
  padding: 4px 4px 4px 6px;
  border-radius: var(--r-pill);
  background: var(--glass-hud);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-md);
  white-space: nowrap;
}

/* Signal bars: green / gold / red by round trip. Tap for the connection
   explainer (route + milliseconds). */
.c2-signal {
  flex: none;
  display: inline-flex;
  align-items: flex-end;
  justify-content: center;
  gap: 3px;
  width: 34px;
  height: 32px;
  padding-bottom: 9px;
  border-radius: var(--r-pill);
}

.c2-signal i {
  display: block;
  width: 4px;
  border-radius: 2px;
  background: rgba(255, 242, 216, 0.24);
}

.c2-signal i:nth-child(1) {
  height: 6px;
}

.c2-signal i:nth-child(2) {
  height: 10px;
}

.c2-signal i:nth-child(3) {
  height: 14px;
}

.c2-signal.is-3 i {
  background: var(--green);
}

.c2-signal.is-2 i:nth-child(-n + 2) {
  background: var(--gold);
}

.c2-signal.is-1 i:nth-child(1) {
  background: var(--red);
}

.c2-signal:active {
  background: rgba(255, 242, 216, 0.08);
}

.c2-signal:focus-visible,
.c2-menu-btn:focus-visible,
.c2-view-seg:focus-visible {
  outline: none;
  box-shadow: var(--ring-focus);
}

.c2-identity {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

.c2-identity-dot {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: var(--r-pill);
  background: var(--me);
}

.c2-identity-name {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 24vw;
}

.c2-identity-tag {
  font-weight: var(--w-black);
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  background: rgba(255, 242, 216, 0.1);
  border-radius: 6px;
  padding: 2px 6px;
}

.c2-view-toggle {
  display: flex;
  gap: 3px;
  background: var(--surface-well);
  border-radius: var(--r-pill);
  padding: 3px;
}

.c2-view-seg {
  height: 26px;
  padding: 0 12px;
  white-space: nowrap;
  border-radius: var(--r-pill);
  font-weight: var(--w-heavy);
  font-size: 11px;
  color: var(--cream);
}

.c2-view-seg.is-active {
  box-shadow: inset 0 0 0 2px var(--cream);
  color: var(--cream);
}

.c2-menu-btn {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 15px 0 11px;
  border-radius: var(--r-pill);
  background: var(--surface-raised);
  font-weight: var(--w-heavy);
  font-size: 13px;
  box-shadow: var(--edge-light);
}

.c2-menu-icon {
  display: flex;
}

.c2-menu-btn:active {
  background: var(--divider);
}

/* The briefing's practice: its pill and this phone's I'm ready toggle. */
.c2-practice-pill {
  flex: none;
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--gold);
  border-radius: var(--r-pill);
  padding: 4px 10px;
}

.c2-ready-toggle {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 16px;
  border-radius: var(--r-pill);
  border: 2px solid rgba(255, 242, 216, 0.5);
  background: transparent;
  color: var(--cream);
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 15px;
  white-space: nowrap;
}

.c2-ready-toggle.is-ready {
  border-color: var(--green);
  background: var(--green);
  color: var(--ink);
}

.c2-ready-toggle-check {
  display: flex;
}

.c2-ready-toggle:focus-visible {
  outline: none;
  box-shadow: var(--ring-focus);
}

/* Stick side. */
.c2-stick-zone {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 50%;
  z-index: 2;
}

.c2-stage.is-lefty .c2-stick-zone {
  left: auto;
  right: 0;
}

.c2-stick {
  position: relative;
  width: 100%;
  height: 100%;
  touch-action: none;
  user-select: none;
}

/* Idle, the stick is a dimmed ghost where the thumb rests: it says "the
   stick lives here", not "press exactly here". A touch anywhere on this half
   brings it under the finger at full strength (a quick pop), and on lift it
   glides back and dims. */
.c2-stick-ring {
  position: absolute;
  width: 144px;
  height: 144px;
  border-radius: var(--r-pill);
  border: 2px dashed rgba(255, 242, 216, 0.3);
  background: radial-gradient(circle, rgba(255, 242, 216, 0.07), rgba(255, 242, 216, 0) 68%);
  display: grid;
  place-items: center;
  transform: translate(-50%, -50%);
  scale: 0.9;
  opacity: 0.75;
  transition:
    scale var(--dur-med) var(--ease-pop),
    opacity var(--dur-med) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
  pointer-events: none;
  /* A thumb moves it by `translate` alone, on its own layer: no layout or
     repaint per move (joystick.rs). */
  will-change: transform, translate;
}

.c2-stick.is-active .c2-stick-ring {
  scale: 1;
  opacity: 1;
  border-style: solid;
  border-color: rgba(255, 242, 216, 0.4);
}

.c2-stick-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--r-pill);
  background: rgba(255, 242, 216, 0.22);
}

/* The stick's role (MOVE, AIM, FLIGHT), under the ring's down arrow. */
.c2-stick-label {
  position: absolute;
  top: calc(100% + 48px);
  left: 50%;
  transform: translateX(-50%);
  font-weight: var(--w-black);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: rgba(255, 242, 216, 0.55);
  white-space: nowrap;
}

/* Menus move the big screen's focus in four directions; the arrow for the step being
   taken lights in the player's colour. */
.c2-stick-arrow {
  position: absolute;
  width: 0;
  height: 0;
  border: 8px solid transparent;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.c2-stick-arrow.is-up {
  top: -42px;
  left: 50%;
  transform: translateX(-50%);
  border-top-width: 0;
  border-bottom-color: rgba(255, 242, 216, 0.3);
}

.c2-stick-arrow.is-down {
  bottom: -42px;
  left: 50%;
  transform: translateX(-50%);
  border-bottom-width: 0;
  border-top-color: rgba(255, 242, 216, 0.3);
}

.c2-stick-arrow.is-left {
  left: -42px;
  top: 50%;
  transform: translateY(-50%);
  border-left-width: 0;
  border-right-color: rgba(255, 242, 216, 0.3);
}

.c2-stick-arrow.is-right {
  right: -42px;
  top: 50%;
  transform: translateY(-50%);
  border-right-width: 0;
  border-left-color: rgba(255, 242, 216, 0.3);
}

.c2-stick-arrow.is-up.is-lit {
  border-bottom-color: var(--me);
}

.c2-stick-arrow.is-down.is-lit {
  border-top-color: var(--me);
}

.c2-stick-arrow.is-left.is-lit {
  border-right-color: var(--me);
}

.c2-stick-arrow.is-right.is-lit {
  border-left-color: var(--me);
}

.c2-stick-knob {
  position: absolute;
  width: 60px;
  height: 60px;
  border-radius: var(--r-pill);
  background: var(--me);
  box-shadow:
    inset 0 3px 0 rgba(255, 255, 255, 0.32),
    inset 0 -4px 7px rgba(8, 9, 12, 0.28),
    0 6px 14px rgba(0, 0, 0, 0.35);
  transform: translate(-50%, -50%);
  scale: 0.84;
  opacity: 0.5;
  transition:
    scale var(--dur-med) var(--ease-pop),
    opacity var(--dur-med) var(--ease-out);
  pointer-events: none;
  will-change: transform, translate;
}

.c2-stick.is-active .c2-stick-knob {
  scale: 1;
  opacity: 1;
}

/* Back to rest after a lift (never while a thumb drags it). */
.c2-stick.is-returning .c2-stick-ring,
.c2-stick.is-returning .c2-stick-knob {
  transition:
    translate 220ms var(--ease-out),
    scale var(--dur-med) var(--ease-out),
    opacity var(--dur-med) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out);
}

.c2-no-stick {
  position: absolute;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  font-weight: var(--w-heavy);
  font-size: 13px;
  color: rgba(255, 242, 216, 0.34);
  pointer-events: none;
}

/* Ready in the lobby: a green check, the word, who starts it, in the pad's
   middle (fixed to the viewport: the button half holds it). */
.c2-ready {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  pointer-events: none;
  text-align: center;
}

.c2-ready-check {
  display: grid;
  place-items: center;
  width: 60px;
  height: 60px;
  margin-bottom: 6px;
  border-radius: var(--r-pill);
  background: var(--green);
  color: var(--ink);
  box-shadow: var(--shadow-md);
}

.c2-ready strong {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 22px;
  color: var(--green);
}

.c2-ready span {
  font-weight: var(--w-bold);
  font-size: 13px;
  color: var(--text-dim);
}

/* Button side: the whole half is live (touches route to the nearest
   button), so the buttons themselves take no pointer events. */
.c2-action-zone {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 50%;
  z-index: 3;
  touch-action: none;
}

.c2-stage.is-lefty .c2-action-zone {
  right: auto;
  left: 0;
}

.c2-btn {
  --d: calc(24 * var(--u));
  --x: 0px;
  --y: 0px;
  --btn-fill: var(--surface-raised);
  --btn-fg: var(--cream);
  position: absolute;
  width: var(--d);
  height: var(--d);
  right: calc(var(--side-r) + var(--x) - var(--d) / 2);
  bottom: calc(var(--side-b) + var(--y) - var(--d) / 2);
  border-radius: var(--r-pill);
  background: var(--btn-fill);
  color: var(--btn-fg);
  box-shadow:
    inset 0 2px 0 rgba(255, 242, 216, 0.1),
    0 6px 14px rgba(8, 9, 12, 0.28);
  pointer-events: none;
  transition:
    box-shadow 60ms var(--ease-out),
    background 60ms var(--ease-out);
}

.c2-stage.is-lefty .c2-btn {
  right: auto;
  left: calc(var(--side-l) + var(--x) - var(--d) / 2);
}

.c2-btn.is-a {
  --d: calc(32 * var(--u));
  --x: calc(20 * var(--u));
  --y: calc(25 * var(--u));
  --btn-fill: var(--me);
  --btn-fg: var(--ink);
  box-shadow:
    inset 0 3px 0 rgba(255, 255, 255, 0.28),
    0 6px 14px rgba(8, 9, 12, 0.32);
}

.c2-btn.is-b {
  --x: calc(13 * var(--u));
  --y: calc(61 * var(--u));
}

.c2-btn.is-x {
  --x: calc(60 * var(--u));
  --y: calc(19 * var(--u));
}

.c2-btn.is-y {
  --d: calc(21 * var(--u));
  --x: calc(53 * var(--u));
  --y: calc(55 * var(--u));
}

.c2-btn.is-ready {
  --btn-fill: var(--green);
  --btn-fg: var(--ink);
}

/* Canon press: sunken and a shade darker — never brighter, never scaled. */
.c2-btn.is-pressed {
  background: color-mix(in srgb, var(--btn-fill) 76%, #08090c);
  box-shadow: inset 0 5px 12px rgba(8, 9, 12, 0.5);
}

.c2-stage.is-overlay .c2-btn {
  opacity: 0.62;
}

.c2-btn-face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
}

.c2-btn-icon {
  display: flex;
}

.c2-btn-label {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: clamp(13px, calc(3.6 * var(--u)), 17px);
  line-height: 1;
}

.c2-btn.is-a .c2-btn-label {
  font-size: clamp(17px, calc(4.9 * var(--u)), 22px);
}

.c2-btn.is-y .c2-btn-label {
  font-size: clamp(12px, calc(3.3 * var(--u)), 15px);
}

/* The gamepad letter, so the big screen's briefing glyphs map onto the pad. */
.c2-btn-badge {
  position: absolute;
  left: calc(14.6% - 12px);
  top: calc(14.6% - 12px);
  width: 24px;
  height: 24px;
  border-radius: var(--r-pill);
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 13px;
  color: var(--ink);
  box-shadow: 0 0 0 3px rgba(8, 9, 12, 0.55);
}

.c2-stage.is-lefty .c2-btn-badge {
  left: auto;
  right: calc(14.6% - 12px);
}

.c2-btn.is-a .c2-btn-badge {
  background: var(--green);
}

.c2-btn.is-b .c2-btn-badge {
  background: var(--coral);
}

.c2-btn.is-x .c2-btn-badge {
  background: var(--teal);
}

.c2-btn.is-y .c2-btn-badge {
  background: var(--gold);
}

.c2-btn-tag {
  position: absolute;
  left: 50%;
  bottom: -9px;
  transform: translateX(-50%);
  padding: 3px 8px;
  border-radius: var(--r-pill);
  background: #0e3449;
  color: rgba(255, 242, 216, 0.85);
  font-weight: var(--w-black);
  font-size: 9px;
  letter-spacing: 0.12em;
  white-space: nowrap;
}

/* Cooldown: a shade that sweeps away as the button recharges. Its --cool
   is set on it alone, so a frame of the sweep restyles nothing else. */
.c2-btn-cool {
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background: conic-gradient(rgba(8, 9, 12, 0.58) var(--cool, 0%), rgba(8, 9, 12, 0) 0);
}

/* Charge: a gold ring filling from the game's reported charge (its own
   element, like the shade, for the same reason). */
.c2-btn-ring {
  position: absolute;
  inset: -9px;
  border-radius: var(--r-pill);
  background: conic-gradient(var(--gold) var(--charge, 0%), rgba(255, 242, 216, 0.14) 0);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px));
  mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px));
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.c2-btn-ring.is-charging {
  opacity: 1;
}

/* With the jetpack in hand, the ring is its fuel, in the tool's green. */
.c2-btn-ring.is-fuel {
  background: conic-gradient(var(--green) var(--charge, 0%), rgba(255, 242, 216, 0.14) 0);
}

/* Sheets (menu, connection): a backdrop that blocks the pad and closes on
   tap, and one centred card. */
.c2-backdrop {
  position: absolute;
  inset: 0;
  z-index: 7;
  background: rgba(8, 9, 12, 0.5);
  cursor: default;
  animation: c2-fade 0.18s var(--ease-out);
}

@keyframes c2-fade {
  from {
    opacity: 0;
  }
}

.c2-sheet {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 8;
  width: min(540px, calc(100vw - 32px));
  max-height: calc(100% - 20px);
  overflow-y: auto;
  overscroll-behavior: contain;
  touch-action: pan-y;
  padding: 16px 18px;
  border-radius: 22px;
  background: var(--glass);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-pop);
  display: flex;
  flex-direction: column;
  gap: 12px;
  animation: c2-pop 0.22s var(--ease-pop);
}

.c2-sheet.is-narrow {
  width: min(420px, calc(100vw - 32px));
}

@keyframes c2-pop {
  from {
    opacity: 0;
    transform: translate(-50%, calc(-50% - 8px));
  }
  to {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

.c2-sheet-head {
  display: flex;
  align-items: center;
  gap: 10px;
}

.c2-sheet-title {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 22px;
  white-space: nowrap;
}

.c2-sheet-who {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  font-weight: var(--w-bold);
  font-size: 13px;
  color: var(--text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The identity line ellipsizes before the Done button (a narrow phone). */
.c2-sheet-who > span:last-child {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* An open panel's button (How to play) or a switched-on one (Players)
   shows it's on: selected is an outline. */
.c2-pill[aria-expanded="true"],
.c2-pill[aria-pressed="true"] {
  box-shadow: inset 0 0 0 2px var(--cream);
}

.c2-sheet-dot {
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: var(--r-pill);
  background: var(--me);
}

.c2-sheet-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

/* Pause game, How to play and Players in one row (the menu never scrolls). */
.c2-sheet-row.is-three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
}

.c2-sheet-row.is-three > .c2-pill {
  padding: 0 10px;
}

/* Spectating, asked to play: when they will. */
.c2-sheet-note {
  margin: 0;
  font-weight: var(--w-bold);
  font-size: 14px;
  color: var(--text-dim);
}



/* Canon Button pills: secondary (raised), primary (gold), danger (red). */
.c2-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 44px;
  padding: 0 20px;
  border-radius: 14px;
  background: var(--quiet);
  color: var(--cream);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 17px;
  white-space: nowrap;
  transition:
    box-shadow var(--dur-med) var(--ease-out),
    background var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.c2-pill span {
  display: inline-flex;
}

.c2-pill:active {
  transform: translateY(1px) scale(0.985);
  background: var(--quiet-hover);
}

.c2-pill:focus-visible,
.c2-text:focus-visible,
.c2-toggle:focus-visible,
.c2-color-dot:focus-visible {
  outline: none;
  box-shadow: var(--edge-light), var(--ring-focus);
}

.c2-pill.is-go {
  background: var(--action);
  color: var(--text-on-action);
  box-shadow: var(--shadow-md);
}

.c2-pill.is-go:active {
  background: var(--action-press);
}

.c2-pill.is-danger {
  background: rgba(255, 104, 79, 0.18);
  color: var(--danger-text);
}

.c2-pill.is-sm {
  height: 36px;
  padding: 0 16px;
  font-size: 15px;
}

.c2-pill.is-lg {
  height: 52px;
  font-size: 17px;
}

.c2-text {
  min-height: 36px;
  padding: 0 4px;
  font-weight: var(--w-heavy);
  font-size: 14px;
  color: var(--cream);
}

.c2-text.is-danger {
  color: #ff7a86;
}

.c2-confirm {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 16px;
  background: var(--quiet);
}

.c2-confirm strong {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 19px;
}

.c2-confirm > span {
  font-weight: var(--w-bold);
  font-size: 14px;
  color: var(--text-dim);
}

.c2-help {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border-radius: var(--r-md);
  /* A card you read is paper, as the big screen's rules card. */
  background: var(--paper);
  color: var(--ink);
}

.c2-help-goal {
  margin: 0;
  font-weight: var(--w-bold);
  font-size: 13px;
  line-height: 1.45;
}

.c2-help-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-weight: var(--w-bold);
  font-size: 13px;
  line-height: 1.4;
}

.c2-help-item {
  display: grid;
  grid-template-columns: 3.4em 1fr;
  gap: 8px;
  align-items: baseline;
}

.c2-help-item b {
  font-weight: var(--w-black);
  color: var(--paper-label);
}

.c2-help-item strong {
  font-weight: var(--w-black);
  margin-right: 6px;
}

.c2-help-hint,
.c2-help-note {
  color: var(--paper-text2);
}

.c2-help-note {
  margin: 0;
  font-weight: var(--w-bold);
  font-size: 12px;
  line-height: 1.4;
}

/* Sixteen colours: two even rows of eight (the menu's Color page). */
.c2-colors {
  display: grid;
  grid-template-columns: repeat(8, 30px);
  justify-content: space-between;
  align-items: center;
  gap: 12px 8px;
}

/* Canon ColorSwatch (controls/ColorSwatch). */
.c2-color-dot {
  position: relative;
  width: 30px;
  height: 30px;
  border-radius: var(--r-pill);
  box-shadow: var(--shadow-sm);
  transition:
    box-shadow var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}

.c2-color-dot:active {
  transform: translateY(1px) scale(0.97);
}

.c2-color-dot.is-mine {
  box-shadow:
    0 0 0 3px var(--ink),
    0 0 0 6px var(--cream);
}

.c2-toggles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(146px, 1fr));
  gap: 8px;
}

.c2-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 44px;
  padding: 0 12px;
  border-radius: var(--r-md);
  background: var(--quiet);
  font-weight: var(--w-heavy);
  font-size: 15px;
  white-space: nowrap;
}

.c2-toggle-icon {
  display: flex;
  flex: none;
}

.c2-toggle-label {
  flex: 1;
  text-align: left;
}

.c2-switch {
  position: relative;
  flex: none;
  width: 32px;
  height: 20px;
  border-radius: var(--r-pill);
  background: rgba(255, 242, 216, 0.2);
  transition: background var(--dur-fast) var(--ease-out);
}

.c2-switch::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 14px;
  height: 14px;
  border-radius: var(--r-pill);
  background: var(--cream);
  transition: transform var(--dur-fast) var(--ease-out);
}

.c2-toggle.is-on .c2-switch {
  background: var(--gold);
}

.c2-toggle.is-on .c2-switch::after {
  transform: translateX(12px);
  background: var(--ink);
}

/* Connection explainer: what the signal bars mean, in plain words. */
.c2-route-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: var(--r-pill);
  color: var(--text-dim);
  background: rgba(255, 242, 216, 0.1);
}

.c2-route-badge.is-direct {
  color: var(--green);
  background: rgba(63, 192, 122, 0.18);
}

.c2-route-badge.is-relay {
  color: var(--gold);
  background: rgba(255, 200, 87, 0.18);
}

.c2-route-copy {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--cream);
}

.c2-route-tip {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.45;
  font-weight: var(--w-bold);
  color: var(--gold);
}

.c2-route-note {
  margin: 0;
  font-weight: var(--w-heavy);
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

.c2-route-note.is-build {
  opacity: 0.75;
}

/* ---------------------------------------------------------------- */
/* C3 · Connection status only                                        */

.c3-overlay {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  background: rgba(8, 9, 12, 0.72);
}

/* Lost: calm, not an alarm — the game may simply have closed. */
.c3-overlay.is-lost {
  background: rgba(8, 9, 12, 0.94);
}

.c3-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
  padding: 20px;
}

.c3-icon {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: var(--r-pill);
  background: var(--surface-raised);
  color: var(--gold);
}

.c3-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 6px;
}

/* Canon Spinner (hud/Spinner): 30px → 2.6px band (max(2, size×0.085)),
   dim track, gold head. */
.c3-spinner {
  width: 30px;
  height: 30px;
  border: 2.6px solid var(--text-dim);
  border-top-color: var(--gold);
  border-radius: var(--r-pill);
  animation: c3-spin 1s linear infinite;
}

@keyframes c3-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .c3-spinner {
    animation-duration: 2.2s;
  }
}

/* Reduced-flash clamps every animation to one 0.001s play (tokens.css) —
   that would freeze the spinner into a static ring. Slow it instead:
   connection progress must stay visibly alive. */
:root[data-reduced-flash="1"] .c3-spinner {
  animation-duration: 2.2s !important;
  animation-iteration-count: infinite !important;
}

.c3-title {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 26px;
}

.c3-copy {
  font-weight: var(--w-bold);
  font-size: 13.5px;
  color: var(--text-dim);
  max-width: 36ch;
}

/* C1 · joined beat — a short "you're in" flash over the fresh gamepad. */
.c1-joined-beat {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 18;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: calc(100vw - 32px);
  padding: 14px 20px;
  border-radius: 16px;
  background: var(--glass-hud);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  box-shadow: var(--shadow-float);
  pointer-events: none;
  animation: c1-beat 2.2s ease-in-out both;
}

.c1-joined-check {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--r-pill);
  background: var(--me, var(--green));
  color: var(--ink);
}

.c1-joined-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.c1-joined-copy strong {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: 18px;
}

.c1-joined-copy span {
  font-weight: var(--w-bold);
  font-size: 12.5px;
  color: var(--text-dim);
}

@keyframes c1-beat {
  0% {
    opacity: 0;
    transform: translate(-50%, -46%) scale(0.94);
  }
  12%,
  82% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -52%) scale(0.98);
  }
}

:root[data-reduced-flash="1"] .c1-joined-beat {
  animation: none;
}

/* ------------------------------------------------------------------ */
/* A game's own screen (Keyword's keymaster's, say). The stick and */
/* buttons give way to a board of tiles (as on the big screen) and a  */
/* panel beside it (above it, upright); the top bar and its sheets stay. */

.c2-stage.is-custom .c2-stick-zone,
.c2-stage.is-custom .c2-action-zone {
  display: none;
}

.gs {
  position: absolute;
  inset: calc(max(8px, env(safe-area-inset-top)) + 54px) var(--side-r) var(--side-b) var(--side-l);
  z-index: 2;
  display: flex;
  gap: 10px;
  align-items: stretch;
  /* A tile's mark of a blackout: a snuffed flame (the big screen's
     Icon::FlameOff). */
  --flame-off: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cmask id='s'%3E%3Crect width='24' height='24' fill='white'/%3E%3Cpath d='M4 3l16.5 16.5' stroke='black' stroke-width='4.8' stroke-linecap='round'/%3E%3C/mask%3E%3Cpath mask='url(%23s)' d='M12 2c1.5 3 6.5 6 6.5 11.5a6.5 6.5 0 0 1-13 0c0-3 2-4.6 3-6.6.5 2 1.5 3 2.5 3-.5-3-.5-5.4 1-7.9Z'/%3E%3Cpath d='M4 3l16.5 16.5' stroke='black' stroke-width='2.4' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* The board: rows of tiles, centred. Hexagons are pointy-topped, each row
   tucked a quarter of a hexagon into the one above. Sized off the board
   itself (container units, --tile-w and --tile-h from the phone), so it
   fits either way up. */
.gs-board {
  flex: 1.9 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  container-type: size;
}

.gs:not(.has-board) .gs-board {
  display: none;
}

.gs-row {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: 1cqw;
}

.gs-row + .gs-row {
  margin-top: calc(var(--tile-h) * -0.25 + 1cqw);
}

.gs-board.is-square .gs-row + .gs-row {
  margin-top: 1cqw;
}

.gs-tile {
  --edge: rgba(8, 9, 12, 0.55);
  position: relative;
  flex: none;
  width: var(--tile-w);
  height: var(--tile-h);
  padding: 3px;
  background: var(--edge);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
}

.gs-face {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 100%;
  height: 100%;
  padding: 0 4px;
  background: #f3e8cf;
  color: var(--ink);
  clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
  font-family: var(--font-body);
  font-weight: var(--w-black);
  font-size: clamp(9px, 2.3cqw, 15px);
  letter-spacing: 0.01em;
  line-height: 1.05;
  transition: background 200ms ease;
}

.gs-board.is-square .gs-tile,
.gs-board.is-square .gs-face {
  clip-path: none;
  border-radius: var(--r-md);
}

.gs-tile-text {
  max-width: 100%;
  overflow: hidden;
  text-overflow: clip;
  white-space: nowrap;
}

/* An edge in a colour (the key, on a keymaster's board), and a mark on
   the face (the blackout word's snuffed flame). */
.gs-tile.has-edge { padding: 5px; }
.gs-tile.has-flame-off .gs-face::before {
  content: "";
  width: 1.1em;
  height: 1.1em;
  background: currentColor;
  -webkit-mask: var(--flame-off) center / contain no-repeat;
  mask: var(--flame-off) center / contain no-repeat;
}

/* A tile locking in. */

.gs-tile.is-pulse {
  padding: 5px;
  animation: gs-pulse 0.5s ease-in-out infinite alternate;
}

@keyframes gs-pulse {
  from { background: var(--gold); }
  to { background: #fff1b8; }
}

/* Reduced flash stills the pulse (tokens.css): a steady gold edge. */
:root[data-reduced-flash="1"] .gs-tile.is-pulse { background: var(--gold); }

.gs-dots {
  display: flex;
  gap: 3px;
}

.gs-dots i {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--ink);
}

/* The panel: pills, then lines, notes, a card, a form and buttons. */
.gs-panel {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  color: var(--cream);
}

.gs-pills {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 6px;
  font-family: var(--font-body);
  font-weight: var(--w-black);
  font-size: 12px;
}

.gs-pill {
  padding: 3px 9px;
  border-radius: var(--r-pill);
}

.gs-label {
  padding: 3px 10px;
  text-transform: uppercase;
  letter-spacing: var(--ls-caps);
}

.gs-counts {
  display: inline-flex;
  gap: 4px;
}

.gs-count {
  min-width: 22px;
  padding: 3px 6px;
}

.gs-clock {
  font-variant-numeric: tabular-nums;
}

.gs-clock.is-low { color: var(--red) !important; }

.gs-main {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
}

.gs-line {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: clamp(15px, 4.4vmin, 20px);
}

.gs-note {
  font-family: var(--font-body);
  font-weight: var(--w-bold);
  font-size: 12.5px;
  color: var(--label);
}

.gs-note.gs-error,
.gs-error {
  color: var(--red);
}

.gs-heading {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: clamp(20px, 6vmin, 28px);
}

.gs-card {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  max-width: 100%;
  padding: 6px 8px 6px 16px;
  border: 3px solid var(--coral);
  border-radius: var(--r-lg);
  background: #f3e8cf;
  color: var(--ink);
}

.gs-card-word {
  font-family: var(--font-display);
  font-weight: var(--w-medium);
  font-size: clamp(20px, 6.5vmin, 30px);
  letter-spacing: 0.02em;
  overflow: hidden;
  text-overflow: ellipsis;
}

.gs-card-number {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--coral);
  color: var(--cream);
  font-family: var(--font-display);
  font-size: 20px;
}

/* A form: a field, a number stepper and a give button. */
.gs-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
  padding: 10px 12px;
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: 0 6px 18px rgba(8, 9, 12, 0.5);
}

.gs-form-label {
  font-family: var(--font-body);
  font-weight: var(--w-black);
  font-size: 11px;
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--gold);
}

.gs-form-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 8px;
}

.gs-input {
  flex: 1 1 100%;
  min-width: 0;
  height: 42px;
  padding: 0 12px;
  border: 2px solid var(--divider);
  border-radius: var(--r-md);
  background: var(--surface-well);
  color: var(--cream);
  font-family: var(--font-display);
  font-size: 20px;
  touch-action: manipulation;
}

.gs-input.is-caps { text-transform: uppercase; }

.gs-input:focus {
  outline: none;
  border-color: var(--cream);
}

.gs-stepper {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 4px;
  border-radius: var(--r-md);
  background: var(--surface-well);
}

.gs-step {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  background: var(--surface-raised);
  color: var(--cream);
  font-family: var(--font-display);
  font-size: 18px;
  touch-action: manipulation;
}

.gs-step:disabled { opacity: 0.35; }

.gs-number {
  min-width: 18px;
  font-family: var(--font-display);
  font-size: 20px;
  color: var(--cream);
}

.gs-give {
  flex: 1;
  min-height: 40px;
  padding: 0 14px;
  border-radius: var(--r-pill);
  background: var(--gold);
  color: var(--ink);
  font-family: var(--font-display);
  font-size: 15px;
  white-space: nowrap;
  touch-action: manipulation;
}

.gs-give:disabled { opacity: 0.55; }

.gs-button {
  flex: none;
  margin-top: 6px;
  padding: 0 22px;
}

.gs-button.is-done {
  background: transparent;
  color: var(--gold);
  box-shadow: inset 0 0 0 2px var(--gold);
}

/* Upright: the panel on top, the board below. How the phone is held, from
   its screen (`data-orient`), never the page's shape: the form's keyboard
   shrinks the page on some browsers. */
:root[data-orient="portrait"] .gs { flex-direction: column; }
:root[data-orient="portrait"] .gs-panel { order: -1; flex: none; }
:root[data-orient="portrait"] .gs-board { flex: 1 1 0; min-height: 0; }

/* The phone keyboard is up: the panel rides to the top, over the board. */
.gs.is-typing .gs-panel {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, 0);
  width: min(96%, 560px);
  z-index: 3;
}

.gs.is-typing .gs-tile {
  opacity: 0.35;
}
