/* base.css — page chrome: shell, scoreboard, revealed-word panel, dev bar.

   THE THREE-ZONE LAYOUT is the load-bearing decision here, and it is portrait-first:

       scoreboard      compact, sticky
       board           flexes to fill whatever is left
       revealed panel  FIXED height, reserved, never an overlay

   In LANDSCAPE the same three zones re-flow into two columns, with the board taking the
   full height on the left and the panels stacked beside it. This is not decoration: the
   diamond is 8 rows tall and 4 columns wide, so its stage is about 0.37 as wide as it is
   tall. Stacked on a wide screen, the board is height-starved and the cards end up tiny
   while most of the viewport sits empty either side. Beside them, the board gets the
   whole height and the cards roughly double.

   The revealed-word panel is a reserved region rather than a floating overlay for two
   reasons. An overlay would cover board cards on a small screen, exactly where the board
   cards are already smallest and the panel matters most. And a variable-height panel would
   make the board resize every time a card is revealed, so cards would appear to jump on
   every single turn. Reserving the space costs a little room and buys a stable board. */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--vm-bg);
  color: var(--vm-text);
  font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  -webkit-text-size-adjust: 100%;
}

body {
  display: flex;
  flex-direction: column;
  overflow: hidden;               /* the board fits the viewport; the page never scrolls */
}

/* ------------------------------------------------------------------ shell */

.vm-shell {
  display: grid;
  gap: var(--vm-gap);
  padding: var(--vm-pad);
  height: 100%;
  max-width: 1100px;
  margin: 0 auto;
  width: 100%;

  /* Portrait / narrow: the three zones stacked, board in the flexible middle row.
     `progress` sits directly under the chrome row, and `status` now comes BEFORE
     `revealed` — the line telling you whose turn it is answers the question a player has
     before they read the two words, not after. */
  grid-template-areas: 'account' 'progress' 'score' 'board' 'status' 'revealed';
  grid-template-rows: auto auto auto minmax(0, 1fr) auto auto;
  grid-template-columns: minmax(0, 1fr);
  min-width: 0;
}

/* Every grid item needs an explicit min-width: setting the TRACK to minmax(0, 1fr) is
   not enough, because a grid item's own default min-width is auto and it will happily
   overflow its track. With body{overflow:hidden} that overflow is clipped in silence,
   which is how the first mobile render lost the dealer badge, half the revealed panel
   and a button off the right edge. */
.vm-scoreboard { grid-area: score;    min-width: 0; }
.vm-board      { grid-area: board;    min-width: 0; }
.vm-revealed   { grid-area: revealed; min-width: 0; }
.vm-status     { grid-area: status;   min-width: 0; }
.vm-progress   { grid-area: progress; min-width: 0; }

/* ------------------------------------------------------------ scoreboard */

.vm-scoreboard {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: var(--vm-gap);
  background: var(--vm-surface);
  border: 1px solid var(--vm-border);
  border-radius: var(--vm-radius);
  padding: clamp(6px, 1.2vw, 12px) clamp(10px, 2vw, 16px);
}

.vm-player {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

/* Name and badge are siblings, not nested: the name ellipsises when space is tight, the
   badge never does. Nesting the badge inside the ellipsised name clipped it away instead. */
.vm-player-head {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

.vm-player-name {
  font-size: clamp(11px, 2.4vw, 14px);
  font-weight: 600;
  color: var(--vm-text-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 0 1 auto;
}

.vm-player-score {
  font-size: clamp(20px, 5vw, 30px);
  font-weight: 800;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* The active player is marked by a badge AND a border, never by colour alone. */
.vm-player.is-active .vm-player-name { color: var(--vm-accent); }
.vm-player.is-active {
  border-left: 3px solid var(--vm-accent);
  padding-left: 8px;
  margin-left: -3px;
}

.vm-badge {
  display: inline-block;
  flex: 0 0 auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--vm-surface-2);
  color: var(--vm-text-dim);
  border: 1px solid var(--vm-border);
}

.vm-badge.is-dealer { background: var(--vm-accent); color: #04121f; border-color: var(--vm-accent); }

.vm-meta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
  padding: 0 clamp(4px, 1.5vw, 14px);
  border-left: 1px solid var(--vm-border);
  border-right: 1px solid var(--vm-border);
  min-width: 0;
  flex: 0 1 auto;
  overflow: hidden;
}

.vm-meta-line {
  font-size: clamp(10px, 2.2vw, 12px);
  color: var(--vm-text-dim);
  white-space: nowrap;
}

.vm-meta-line strong { color: var(--vm-text); font-weight: 700; }

.vm-streak { display: flex; gap: 3px; align-items: center; }

/* Flames, not circles — see ui/scoreboard.js's buildStreak for why. The unlit state is a
   dim outline of the same shape rather than a different shape, so the row reads as one
   meter filling up rather than as two kinds of thing sitting side by side. */
.vm-pip {
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(11px, 2.6vw, 14px);
  height: clamp(11px, 2.6vw, 14px);
  color: var(--vm-border);
  transition: color 220ms ease, transform 220ms ease;
}

.vm-pip svg { width: 100%; height: 100%; display: block; }

.vm-pip.is-lit {
  /* Warm, and deliberately NOT the blue accent: a streak is heat, and it also needs to be
     distinguishable from the active-player marker sitting a few pixels away. */
  color: var(--vm-warn);
  transform: scale(1.12);
}

/* ------------------------------------------------- revealed-word panel */

.vm-revealed {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--vm-gap);
  align-items: stretch;
  min-height: clamp(74px, 15vh, 128px);
}

.vm-revealed-slot {
  display: flex;
  overflow: hidden;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  padding: clamp(6px, 1.4vw, 12px) clamp(8px, 2vw, 16px);
  border-radius: var(--vm-radius);
  border: 2px solid var(--vm-border);
  background: var(--vm-surface);
  min-width: 0;
  text-align: center;
}

.vm-revealed-slot.is-empty { border-style: dashed; opacity: 0.55; }

.vm-revealed-form {
  align-self: center;
  font-size: clamp(10px, 2.2vw, 13px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* A solid chip, coloured inline from the card's ink. Small text must not sit directly
     on a card colour — the fixed green tops out near 4.34:1 against any ink. */
  padding: 2px 8px;
  border-radius: 999px;
}

/* The whole point of this panel: the word, big. Scales with the viewport and shrinks on
   long alternatives like "am - is - are" rather than overflowing. */
.vm-revealed-word {
  font-size: clamp(20px, 7vw, 44px);
  font-weight: 800;
  line-height: 1.05;
  overflow-wrap: break-word;
  hyphens: auto;
}

.vm-revealed-word.is-long { font-size: clamp(15px, 4.6vw, 30px); }

.vm-revealed-hint {
  grid-column: 1 / -1;
  text-align: center;
  font-size: clamp(10px, 2.2vw, 12px);
  color: var(--vm-text-dim);
}

/* -------------------------------------------------------------- status */

.vm-status {
  margin: 0;
  text-align: center;
  font-size: clamp(12px, 2.6vw, 15px);
  font-weight: 600;
  line-height: 1.35;
  color: var(--vm-text);
  /* Reserved height, like the revealed panel: a message appearing and disappearing must
     not resize the board above it. Two lines' worth, since the longest messages wrap. */
  min-height: 2.7em;
  display: flex;
  align-items: center;
  justify-content: center;
}

.vm-status.is-empty { visibility: hidden; }

/* ------------------------------------------------- round progress (segmented) */

/* One segment per capture the round requires, filled as they land. Modelled on the
   segmented bar in public/tools/quizer (see its .questions-progress-bar): equal-flex
   segments, a small gap, a short transition, and the CURRENT segment marked by brightness
   plus a glow rather than by hue alone — so it survives a colourblind viewer, same rule
   the rest of this game follows. */
.vm-progress[hidden] { display: none; }

.vm-progress {
  display: flex;
  gap: 4px;
  width: 100%;
  height: 8px;
  align-items: stretch;
}

.vm-progress-segment {
  flex: 1;
  min-width: 0;
  background: var(--vm-surface-2);
  border: 1px solid var(--vm-border);
  border-radius: 3px;
  transition: background 300ms ease, box-shadow 300ms ease, border-color 300ms ease;
}

.vm-progress-segment.is-captured {
  background: var(--vm-accent);
  border-color: var(--vm-accent);
}

.vm-progress-segment.is-current {
  background: color-mix(in srgb, var(--vm-accent) 28%, transparent);
  border-color: var(--vm-accent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--vm-accent) 55%, transparent);
}

/* ------------------------------------------------------- the match clock */

.vm-clock[hidden] { display: none; }

.vm-clock {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;   /* the width must not jitter as digits change */
  color: var(--vm-text-dim);
  padding: 4px 9px;
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  background: var(--vm-surface);
}

/* --------------------------------------------- shared header icon buttons */

.vm-icon-button {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  background: var(--vm-surface);
  color: var(--vm-text-dim);
  cursor: pointer;
}

.vm-icon-button:hover { color: var(--vm-text); border-color: var(--vm-text-dim); }
.vm-icon-button:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

/* One icon, two states: the "exit" arrows only appear once fullscreen is actually on, so
   the button always shows what pressing it will DO rather than what is currently true. */
.vm-fs-exit { display: none; }
.vm-icon-button[aria-pressed="true"] .vm-fs-enter { display: none; }
.vm-icon-button[aria-pressed="true"] .vm-fs-exit { display: block; }
.vm-icon-button[aria-pressed="true"] { color: var(--vm-accent); border-color: var(--vm-accent); }

/* --------------------------------------------------- the round banner */

/* Slides down from the top edge, waits, slides back. NEVER takes pointer events and never
   sits over the board's middle — the same "nothing blocks play" rule the dealer-draw note
   and the forfeit banner already follow. */
.vm-round-banner[hidden] { display: none; }

.vm-round-banner {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 950;              /* above cards (max 999 selected), below overlays (1000) */
  pointer-events: none;
  padding: 10px 26px;
  border: 1px solid var(--vm-accent);
  border-top: none;
  border-radius: 0 0 var(--vm-radius) var(--vm-radius);
  background: var(--vm-surface);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.45);
  font-size: clamp(14px, 3vw, 19px);
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--vm-accent);
  white-space: nowrap;
  transform: translate(-50%, -100%);
  transition: transform 320ms cubic-bezier(0.2, 0.9, 0.3, 1.2);
}

.vm-round-banner.is-in { transform: translate(-50%, 0); }

@media (prefers-reduced-motion: reduce) {
  .vm-round-banner { transition-duration: 1ms; }
  .vm-menu-field, .vm-menu-field.is-collapsed { transition-duration: 1ms; }
  .vm-switch, .vm-switch-knob { transition-duration: 1ms; }
  .vm-skeleton { animation: none; }
}

/* ------------------------------------------------ the single loading state */

/* Shapes roughly where the menu's own title, fields and buttons will be, so the swap into
   the real thing reads as the same screen resolving rather than as a different screen
   arriving. It is one state from auth to ready — see auth/gate.js. */
.vm-loading {
  width: min(26rem, 100%);
  padding: clamp(20px, 5vw, 34px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.vm-skeleton {
  border-radius: 8px;
  background: linear-gradient(90deg,
    var(--vm-surface-2) 25%,
    color-mix(in srgb, var(--vm-surface-2) 60%, var(--vm-border)) 50%,
    var(--vm-surface-2) 75%);
  background-size: 200% 100%;
  animation: vm-skeleton-sweep 1.4s ease-in-out infinite;
}

.vm-skeleton-title { height: 26px; width: 62%; align-self: center; }
.vm-skeleton-line { height: 38px; }
.vm-skeleton-block { height: 92px; }

@keyframes vm-skeleton-sweep {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}

.vm-loading-text {
  margin: 4px 0 0;
  text-align: center;
  font-size: 12px;
  color: var(--vm-text-dim);
}

/* ------------------------------------------------------ the nickname prompt */

.vm-nickname {
  width: min(24rem, 100%);
  padding: clamp(20px, 5vw, 32px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  box-shadow: 0 18px 50px rgb(0 0 0 / 0.55);
  text-align: left;
}

.vm-nickname-title {
  margin: 0 0 8px;
  font-size: clamp(18px, 4.6vw, 23px);
  font-weight: 800;
  color: var(--vm-accent);
}

.vm-nickname-hint {
  margin: 0 0 18px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--vm-text-dim);
}

.vm-nickname .vm-menu-input { margin-bottom: 10px; }
.vm-nickname-error { margin: 0 0 12px; font-size: 12px; color: var(--vm-danger); }
.vm-nickname-error[hidden] { display: none; }

/* --------------------------------------------------- the settings panel */

.vm-settings {
  width: min(20rem, 100%);
  padding: clamp(18px, 4vw, 26px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  box-shadow: 0 18px 50px rgb(0 0 0 / 0.55);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.vm-settings-title {
  margin: 0 0 4px;
  font-size: 18px;
  font-weight: 800;
  color: var(--vm-accent);
  text-align: center;
}

.vm-settings-action {
  width: 100%;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--vm-text);
  background: var(--vm-surface-2);
  border: 1px solid var(--vm-border);
  border-radius: 10px;
  padding: 11px 14px;
  min-height: 44px;
  cursor: pointer;
}

.vm-settings-action:hover { border-color: var(--vm-accent); }
.vm-settings-action:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

.vm-settings-note {
  margin: 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--vm-text-dim);
  text-align: center;
}

.vm-settings-close {
  margin: 4px auto 0;
  font: inherit;
  font-size: 12px;
  color: var(--vm-text-dim);
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
  min-height: 32px;
}

/* ------------------------------------------- the developer timing panel

   Deliberately plain and deliberately ugly: it is a tool, it is temporary, and looking
   unfinished is the point — nobody should mistake it for part of the game. */
.vm-dev-panel {
  position: fixed;
  bottom: 8px;
  left: 8px;
  z-index: 1100;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px dashed var(--vm-warn);
  border-radius: 6px;
  background: rgb(4 8 16 / 0.9);
  font-size: 11px;
  color: var(--vm-text-dim);
}

.vm-dev-readout { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--vm-warn); }
.vm-dev-note { opacity: 0.7; }

/* --------------------------------------------------- account, gate, menu */

/* Holds the whole "account" grid area (portrait and landscape both name it that — see the
   landscape rule further down). The mute toggle and the identity badge are unrelated
   concerns that happen to share one corner of the chrome, so they get one shared area
   rather than a second named one apiece. */
.vm-header-right {
  grid-area: account;
  display: flex;
  align-items: center;
  gap: 10px;
  justify-self: end;
  min-width: 0;
  max-width: 100%;
}

.vm-account {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--vm-text-dim);
  min-width: 0;
}

.vm-account img { width: 22px; height: 22px; border-radius: 50%; flex: 0 0 auto; }
.vm-account span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The mute toggle. A plain icon button, not tied to sign-in state — it works from the
   moment the page loads, since it only ever talks to render/sound.js's own localStorage
   preference, never to the account. */
.vm-sound-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  background: var(--vm-surface);
  color: var(--vm-text-dim);
  cursor: pointer;
}

.vm-sound-toggle:hover { color: var(--vm-text); border-color: var(--vm-text-dim); }
.vm-sound-toggle:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

/* Muted state reads as OFF, not as an error: dimmer, not red — the danger colour is
   reserved for things actually going wrong (see .vm-gate-error). */
.vm-sound-toggle.is-muted { color: var(--vm-text-dim); border-color: var(--vm-border); opacity: 0.7; }

.vm-sound-mute { display: none; }
.vm-sound-toggle.is-muted .vm-sound-waves { display: none; }
.vm-sound-toggle.is-muted .vm-sound-mute { display: block; }

/* Leave a live online match on purpose (Track B, "B4d"). Quiet by default — it's a real,
   uncommon action, not something to draw the eye to during ordinary play — and only reads
   as a warning on hover/focus, where the player is actually about to use it. */
.vm-leave-match {
  flex: 0 0 auto;
  font: inherit;
  font-size: 11px;
  color: var(--vm-text-dim);
  background: none;
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  padding: 5px 12px;
  min-height: 30px;
  cursor: pointer;
}

.vm-leave-match:hover, .vm-leave-match:focus-visible {
  color: var(--vm-danger);
  border-color: var(--vm-danger);
  outline: none;
}

/* -------------------------------------------------- the forfeit banner (Track B, "B4d") */

/* `display: flex` below out-specifies the browser's own [hidden] { display: none } — the
   exact gotcha .vm-overlay[hidden] already exists for. Without this rule the banner sits
   on screen permanently as an empty orange-bordered pill, which is precisely what it did
   between B4d and this pass. Any new element here that sets `display` needs its own
   [hidden] guard. */
.vm-forfeit-banner[hidden] { display: none; }

.vm-forfeit-banner {
  margin: 0 auto;
  max-width: 26rem;
  padding: 10px 14px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1px solid var(--vm-warn);
  border-radius: var(--vm-radius);
  background: var(--vm-surface);
}

.vm-forfeit-text { margin: 0; font-size: 12px; color: var(--vm-warn); }

.vm-forfeit-claim {
  flex: 0 0 auto;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: #04121f;
  background: var(--vm-warn);
  border: none;
  border-radius: 999px;
  padding: 6px 16px;
  min-height: 32px;
  cursor: pointer;
}

.vm-forfeit-claim:focus-visible { outline: 2px solid var(--vm-warn); outline-offset: 2px; }

.vm-gate-card,
/* Two halves to the overflow fix, and the menu needs both. The padding and the internal
   spacing shrink with the VIEWPORT HEIGHT (not just its width — a wide-but-short laptop
   window is the case that actually overflowed), so on most screens nothing needs to
   scroll at all; then `max-height` + `overflow-y` catch whatever still doesn't fit, so a
   control can never end up below the fold with no way to reach it. */
.vm-menu {
  width: min(26rem, 100%);
  text-align: center;
  padding: clamp(14px, 3.2vh, 34px) clamp(20px, 5vw, 34px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  box-shadow: 0 18px 50px rgb(0 0 0 / 0.55);
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.vm-gate-title, .vm-menu-title {
  margin: 0 0 8px;
  font-size: clamp(21px, 5.4vw, 28px);
  font-weight: 800;
  color: var(--vm-accent);
}

.vm-gate-text { margin: 0 0 22px; color: var(--vm-text-dim); font-size: 14px; line-height: 1.5; }

.vm-gate-button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font: inherit;
  font-weight: 700;
  color: #1f2937;
  background: #fff;
  border: none;
  border-radius: 999px;
  padding: 12px 22px;
  min-height: 44px;
  cursor: pointer;
}

.vm-gate-button:focus-visible { outline: 3px solid var(--vm-accent); outline-offset: 3px; }
.vm-gate-button:disabled, .vm-gate-button.is-busy { opacity: 0.6; cursor: progress; }

.vm-gate-error {
  margin: 16px 0 0;
  color: var(--vm-danger);
  font-size: 13px;
}

/* ------------------------------------------------------------------ menu */

.vm-menu { text-align: left; }
.vm-menu-title { text-align: center; margin-bottom: 20px; }

.vm-menu-field { display: block; margin: 0 0 16px; }
.vm-menu-label {
  display: block;
  margin-bottom: 5px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vm-text-dim);
}

.vm-menu-input {
  width: 100%;
  font: inherit;
  font-size: 15px;
  color: var(--vm-text);
  background: var(--vm-surface-2);
  border: 1px solid var(--vm-border);
  border-radius: 10px;
  padding: 10px 12px;
  min-height: 44px;
}

.vm-menu-input:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 1px; }

.vm-menu-modes { border: none; padding: 0; margin: 0 0 16px; }
.vm-menu-modes legend {
  padding: 0;
  margin-bottom: 5px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vm-text-dim);
}

.vm-menu-mode {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  color: var(--vm-text);
  background: var(--vm-surface-2);
  border: 2px solid var(--vm-border);
  border-radius: 12px;
  padding: 10px 12px;
  margin-bottom: 8px;
  cursor: pointer;
  min-height: 44px;
}

/* Selected state carried by border AND a check, never by colour alone. */
.vm-menu-mode[aria-pressed="true"] { border-color: var(--vm-accent); }
.vm-menu-mode[aria-pressed="true"] .vm-menu-mode-name::after { content: ' \2713'; color: var(--vm-accent); }
.vm-menu-mode:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

.vm-menu-mode-name { display: block; font-weight: 700; font-size: 14px; }
.vm-menu-mode-hint { display: block; font-size: 12px; color: var(--vm-text-dim); margin-top: 2px; }

.vm-menu-online {
  display: block;
  width: 100%;
  margin: 0 0 16px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--vm-accent);
  background: transparent;
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  padding: 10px;
  min-height: 44px;
  cursor: pointer;
}

.vm-menu-online:hover { border-color: var(--vm-accent); }
.vm-menu-online:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

.vm-menu-record {
  margin: 4px 0 18px;
  text-align: center;
  font-size: 13px;
  color: var(--vm-text-dim);
  font-variant-numeric: tabular-nums;
}

.vm-menu-offline {
  margin: -10px 0 16px;
  text-align: center;
  font-size: 12px;
  color: var(--vm-warn);
}

.vm-menu-play {
  display: block;
  width: 100%;
  font: inherit;
  font-weight: 800;
  font-size: 16px;
  color: #04121f;
  background: linear-gradient(135deg, var(--vm-accent), var(--vm-accent-dark));
  border: none;
  border-radius: 999px;
  padding: 13px;
  min-height: 48px;
  cursor: pointer;
}

.vm-menu-play:focus-visible { outline: 3px solid var(--vm-accent); outline-offset: 3px; }

.vm-menu-signout {
  display: block;
  margin: 12px auto 0;
  font: inherit;
  font-size: 12px;
  color: var(--vm-text-dim);
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
  min-height: 32px;
}

/* ------------------------------------------- the public leaderboard opt-in (Track B) */

.vm-menu-lb {
  margin: 0 0 16px;
  padding: 12px 14px;
  border: 1px solid var(--vm-border);
  border-radius: 10px;
  background: var(--vm-surface-2);
}

.vm-menu-lb-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--vm-text);
  cursor: pointer;
}

.vm-menu-lb-row { display: flex; align-items: center; gap: 8px; }
.vm-menu-lb-row .vm-menu-lb-toggle { flex: 1 1 auto; min-width: 0; }
.vm-menu-lb-label { min-width: 0; }

/* The switch: a real checkbox, moved off-screen rather than `display: none` so it keeps
   its keyboard behaviour and its focus ring has something to attach to. The track and knob
   are decoration and say so with aria-hidden — the input is still the control. */
.vm-switch-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

.vm-switch {
  flex: 0 0 auto;
  position: relative;
  display: inline-block;
  width: 38px;
  height: 22px;
  border-radius: 999px;
  background: var(--vm-surface);
  border: 1px solid var(--vm-border);
  transition: background 160ms ease, border-color 160ms ease;
}

.vm-switch-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--vm-text-dim);
  transition: transform 160ms ease, background 160ms ease;
}

.vm-switch-input:checked + .vm-switch {
  background: color-mix(in srgb, var(--vm-accent) 30%, transparent);
  border-color: var(--vm-accent);
}
.vm-switch-input:checked + .vm-switch .vm-switch-knob {
  transform: translateX(16px);
  background: var(--vm-accent);
}
.vm-switch-input:focus-visible + .vm-switch {
  outline: 2px solid var(--vm-accent);
  outline-offset: 2px;
}

/* The info button. Sized to the 24px touch minimum the rest of this UI uses, and marked
   by shape (a circle with an "i") rather than by colour alone. */
.vm-info-button {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  padding: 0;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  font-style: italic;
  line-height: 1;
  color: var(--vm-text-dim);
  background: none;
  border: 1px solid var(--vm-border);
  border-radius: 50%;
  cursor: pointer;
}
.vm-info-button:hover { color: var(--vm-text); border-color: var(--vm-text-dim); }
.vm-info-button:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

.vm-menu-lb-hint {
  margin: 6px 0 0;
  font-size: 12px;
  color: var(--vm-text-dim);
  line-height: 1.4;
}

/* A collapsed field animates away and only then leaves the tab order — `visibility` rides
   a zero-length transition delayed by the full duration, so it flips after the motion
   rather than at the start of it. See ui/menu.js's collapse() for why no JS timer. */
.vm-menu-field.is-collapsed {
  max-height: 0;
  opacity: 0;
  margin-block: 0;
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
  transition:
    max-height 220ms ease,
    opacity 160ms ease,
    margin-block 220ms ease,
    visibility 0s linear 220ms;
}

/* No `overflow: hidden` in the EXPANDED state — it would clip the input's own focus ring,
   which sits outside the box. Only the collapsed state needs to clip, and by then there is
   no focus ring to lose. */
.vm-menu-field {
  max-height: 12rem;
  transition:
    max-height 220ms ease,
    opacity 220ms ease 60ms,
    margin-block 220ms ease;
}

/* Short viewports: tighten the menu's own rhythm before falling back to scrolling. A
   laptop window at 700px tall, or a phone held sideways, fits the whole menu after this —
   scrolling is the safety net, not the normal experience. */
@media (max-height: 780px) {
  .vm-menu-title { margin-bottom: 12px; font-size: clamp(19px, 4.4vw, 23px); }
  .vm-menu-field { margin-bottom: 10px; }
  .vm-menu-modes { margin-bottom: 10px; }
  .vm-menu-mode { padding-block: 8px; }
  .vm-menu-lb { margin-bottom: 10px; padding: 9px 12px; }
  .vm-menu-record { margin-block: 8px; }
  .vm-menu-online, .vm-menu-podium { margin-bottom: 10px; padding-block: 9px; }
}

@media (max-height: 620px) {
  .vm-menu-mode-hint { display: none; }   /* the label alone still says which mode it is */
  .vm-menu-title { margin-bottom: 8px; }
}

.vm-menu-lb .vm-menu-field { margin: 12px 0 0; }

.vm-menu-podium {
  display: block;
  width: 100%;
  margin: 0 0 16px;
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--vm-accent);
  background: transparent;
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  padding: 10px;
  min-height: 44px;
  cursor: pointer;
}

.vm-menu-podium:hover { border-color: var(--vm-accent); }
.vm-menu-podium:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

/* ------------------------------------------------------------- game over */

.vm-overlay {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: var(--vm-pad);
  background: rgb(4 8 16 / 0.82);
  /* The fallback half of the overflow fix: whatever the responsive sizing below still
     cannot fit — a short laptop window, a phone in landscape, a long record line — becomes
     scrollable rather than being clipped off the bottom with no way to reach it.
     `place-items: center` centres content that fits and start-aligns what doesn't, which
     is why the panel below also caps its own height rather than relying on this alone. */
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Above every card. A revealed card carries z-index 900 and a selected one 999, and
     .vm-stage does not create a stacking context (position:relative with z-index:auto
     does not), so those compete directly with this. At z-index 100 a revealed card punched
     straight through the veil and sat lit on top of the intro. See STACKING ORDER below. */
  z-index: 1000;
}

/* `display: grid` above out-specifies the browser's own [hidden] { display: none }, so
   without this rule a "hidden" overlay stays on screen: a full-viewport, 82%-opaque veil
   that dims the board AND swallows every tap meant for a card.

   It hid for two phases because automated probes call element.click(), which dispatches
   straight at the target and skips hit-testing entirely — so the game looked playable in
   every scripted run while being completely unclickable by hand. Any overlay added here
   needs this rule, and any check that a control is reachable has to go through
   elementFromPoint rather than a synthetic click. */
.vm-overlay[hidden] { display: none; }

/* STACKING ORDER, in one place because it is otherwise spread across three files:

     1 .. 701   board cards          (slot.row * 100 + col + 1, board.js)
     900        a revealed card      (.vm-slot.is-revealed, board.css)
     999        the selected card    (.vm-slot.is-selected, board.css)
     1000       overlays             (intro, game over)
     1100       the effects layer    (animation.css)

   The effects layer sits ABOVE the overlays on purpose: the victory burst is anchored on
   the result panel and radiates over it, so putting it underneath would hide it entirely
   behind the panel's own background. */

.vm-result {
  width: min(28rem, 100%);
  text-align: center;
  padding: clamp(20px, 5vw, 36px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  box-shadow: 0 18px 50px rgb(0 0 0 / 0.55);
}

.vm-result-title {
  margin: 0 0 6px;
  font-size: clamp(24px, 6vw, 34px);
  font-weight: 800;
  color: var(--vm-accent);
}

.vm-result-why    { margin: 0 0 18px; color: var(--vm-text-dim); font-size: 14px; }
.vm-result-scores { margin: 0 0 4px; font-size: clamp(18px, 4.4vw, 24px); font-weight: 700; font-variant-numeric: tabular-nums; }
.vm-result-meta   { margin: 0 0 22px; color: var(--vm-text-dim); font-size: 13px; }

.vm-result-again {
  font: inherit;
  font-weight: 700;
  color: #04121f;
  background: linear-gradient(135deg, var(--vm-accent), var(--vm-accent-dark));
  border: none;
  border-radius: 999px;
  padding: 12px 28px;
  min-height: 44px;
  cursor: pointer;
}

.vm-result-again:focus-visible { outline: 3px solid var(--vm-accent); outline-offset: 3px; }

.vm-result-unsaved { margin: 0 0 16px; color: var(--vm-warn); font-size: 12px; }

.vm-result-menu {
  display: block;
  margin: 12px auto 0;
  font: inherit;
  font-size: 12px;
  color: var(--vm-text-dim);
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
  min-height: 32px;
}

.vm-error {
  margin: auto;
  max-width: 32rem;
  padding: 20px;
  border: 1px solid var(--vm-danger);
  border-radius: var(--vm-radius);
  background: var(--vm-surface);
  line-height: 1.5;
}

/* --------------------------------------------------------- the leaderboard (Track B) */

.vm-podium {
  width: min(28rem, 100%);
  max-height: min(34rem, 90vh);
  display: flex;
  flex-direction: column;
  text-align: left;
  padding: clamp(20px, 5vw, 30px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  box-shadow: 0 18px 50px rgb(0 0 0 / 0.55);
}

.vm-podium-title {
  margin: 0 0 16px;
  text-align: center;
  font-size: clamp(20px, 5vw, 26px);
  font-weight: 800;
  color: var(--vm-accent);
}

.vm-podium-tabs {
  display: flex;
  gap: 6px;
  margin: 0 0 16px;
  border-bottom: 1px solid var(--vm-border);
}

.vm-podium-tab {
  flex: 1;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--vm-text-dim);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 4px;
  min-height: 40px;
  cursor: pointer;
}

.vm-podium-tab[aria-selected="true"] { color: var(--vm-accent); border-bottom-color: var(--vm-accent); }
.vm-podium-tab:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: -2px; }

.vm-podium-body { flex: 1; overflow-y: auto; min-height: 0; }

.vm-podium-empty {
  margin: 20px 0;
  text-align: center;
  font-size: 13px;
  color: var(--vm-text-dim);
  line-height: 1.5;
}

.vm-podium-list { list-style: none; margin: 0; padding: 0; }

.vm-podium-row {
  display: grid;
  grid-template-columns: 2.6rem 1fr auto;
  align-items: center;
  gap: 8px;
  padding: 8px 4px;
  border-bottom: 1px solid var(--vm-border);
  font-size: 13px;
}

.vm-podium-row:last-child { border-bottom: none; }

.vm-podium-rank { color: var(--vm-text-dim); font-variant-numeric: tabular-nums; }
.vm-podium-name { color: var(--vm-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vm-podium-score { font-weight: 700; font-variant-numeric: tabular-nums; }

.vm-podium-stats {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 10px 12px;
  margin: 8px 0;
}

.vm-podium-stats dt { color: var(--vm-text-dim); font-size: 13px; }
.vm-podium-stats dd {
  margin: 0;
  text-align: right;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.vm-podium-close {
  display: block;
  width: 100%;
  margin-top: 16px;
  font: inherit;
  font-weight: 700;
  color: var(--vm-text);
  background: var(--vm-surface-2);
  border: 1px solid var(--vm-border);
  border-radius: 999px;
  padding: 10px;
  min-height: 44px;
  cursor: pointer;
}

.vm-podium-close:focus-visible { outline: 3px solid var(--vm-accent); outline-offset: 3px; }

/* -------------------------------------------------------- the online lobby (Track B, B4a) */

.vm-lobby {
  width: min(26rem, 100%);
  text-align: center;
  padding: clamp(20px, 5vw, 34px);
  border-radius: var(--vm-radius);
  border: 1px solid var(--vm-border);
  background: var(--vm-surface);
  box-shadow: 0 18px 50px rgb(0 0 0 / 0.55);
}

.vm-lobby-title {
  margin: 0 0 18px;
  font-size: clamp(20px, 5vw, 26px);
  font-weight: 800;
  color: var(--vm-accent);
}

.vm-lobby-error {
  margin: -6px 0 16px;
  color: var(--vm-danger);
  font-size: 13px;
}

.vm-lobby-entry { display: flex; flex-direction: column; gap: 10px; }

.vm-lobby-option {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  color: var(--vm-text);
  background: var(--vm-surface-2);
  border: 2px solid var(--vm-border);
  border-radius: 12px;
  padding: 12px 14px;
  cursor: pointer;
  min-height: 44px;
}

.vm-lobby-option:hover { border-color: var(--vm-accent); }
.vm-lobby-option:focus-visible { outline: 2px solid var(--vm-accent); outline-offset: 2px; }

.vm-lobby-option-name { display: block; font-weight: 700; font-size: 14px; }
.vm-lobby-option-hint { display: block; font-size: 12px; color: var(--vm-text-dim); margin-top: 2px; }

.vm-lobby-code-label {
  margin: 0 0 4px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vm-text-dim);
}

.vm-lobby-code {
  margin: 0 0 20px;
  font-size: clamp(30px, 9vw, 44px);
  font-weight: 800;
  letter-spacing: 0.14em;
  color: var(--vm-accent);
  font-variant-numeric: tabular-nums;
}

.vm-lobby-spinner-wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; }

.vm-lobby-spinner {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 3px solid var(--vm-border);
  border-top-color: var(--vm-accent);
  animation: vm-lobby-spin 900ms linear infinite;
}

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

.vm-lobby-spinner-text { margin: 0; font-size: 13px; color: var(--vm-text-dim); }

.vm-lobby-join { text-align: left; }

.vm-lobby-code-input {
  text-align: center;
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.vm-lobby-join-button {
  display: block;
  width: 100%;
  margin-top: 14px;
  font: inherit;
  font-weight: 800;
  font-size: 16px;
  color: #04121f;
  background: linear-gradient(135deg, var(--vm-accent), var(--vm-accent-dark));
  border: none;
  border-radius: 999px;
  padding: 13px;
  min-height: 48px;
  cursor: pointer;
}

.vm-lobby-join-button:focus-visible { outline: 3px solid var(--vm-accent); outline-offset: 3px; }

.vm-lobby-matched-title {
  margin: 0 0 8px;
  font-size: 18px;
  font-weight: 800;
  color: var(--vm-good);
}

.vm-lobby-matched-names {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--vm-text);
}

.vm-lobby-cancel {
  display: block;
  margin: 18px auto 0;
  font: inherit;
  font-size: 12px;
  color: var(--vm-text-dim);
  background: none;
  border: none;
  text-decoration: underline;
  cursor: pointer;
  min-height: 32px;
}

/* Landscape / wide: panels beside the board so it can use the full height. */
@media (min-width: 700px) and (min-aspect-ratio: 1 / 1) {
  /* Landscape. The board is laid on its side (render/board.js transposes the layout), so
     what it needs is WIDTH, and everything else is arranged to stop competing for it.

         header:  account (who is signed in — and nothing else)
         middle:  both players | board | the two revealed words
         under:                | board | what just happened

     The scores used to live in the header. Moving them into a left rail is what frees the
     middle row to be as tall as the shell allows: a full-width header row costs the board
     its height across the entire screen, while a rail costs it only width it was not using.

     The turn message sits under the revealed words rather than across the foot. The
     active player's highlighted card in the left rail already says whose turn it is; the
     message's other half is what just happened, which is exactly what the revealed words
     are showing. */
  .vm-shell {
    /* `status` above `revealed` in the right rail, matching portrait: whose turn it is is
       read before the words, not after them. `progress` spans the full width under the
       chrome row, where a bar reads as belonging to the whole match rather than to one
       rail. */
    grid-template-areas:
      'account  account account'
      'progress progress progress'
      'players  board    status'
      'players  board    revealed';
    /* The rails take a floor in px so a long name cannot squeeze them to nothing, and a
       ceiling in fr so they never take width the board could use. */
    grid-template-columns:
      minmax(190px, 0.95fr)
      minmax(0, 3.4fr)
      minmax(190px, 0.95fr);
    grid-template-rows: auto auto minmax(0, 1fr) auto;
    max-width: 1500px;
    /* stretch, not center: .vm-board must fill its area for the stage to have a height to
       size against. Items wanting to be centred ask for it individually. */
    align-items: stretch;
  }

  .vm-scoreboard  { grid-area: players; }
  .vm-header-right { grid-area: account; align-self: center; justify-self: end; }
  .vm-board       { grid-area: board; }
  .vm-revealed    { grid-area: revealed; }
  .vm-status      { grid-area: status; }

  /* ---- left rail: both players, stacked ---- */

  .vm-scoreboard {
    /* Column, not row: two player cards above one another with the round meta between
       them, which is the reading order someone actually wants — me, the state of play,
       them. */
    flex-direction: column;
    align-items: stretch;
    gap: clamp(10px, 1.6vh, 18px);
    min-height: 0;
    /* Hug the content and sit level with the middle of the board. Stretched to the full
       rail height the panel is mostly empty box, which reads as something failing to load
       rather than as a deliberately quiet rail. */
    align-self: center;
  }

  .vm-player { flex: 0 0 auto; }

  /* The active player's card is the turn indicator. A full border rather than the
     portrait build's left rule: in a rail the card reads as an object, and an object
     that is "on" is outlined, not underlined. */
  .vm-player.is-active {
    border: 2px solid var(--vm-accent);
    border-radius: var(--vm-radius);
    padding: clamp(6px, 1.1vh, 10px) 10px;
    margin-left: 0;
    background: color-mix(in srgb, var(--vm-accent) 9%, transparent);
  }

  /* Inactive cards keep the same box so switching turns does not shift the rail. */
  .vm-player:not(.is-active) {
    border: 2px solid transparent;
    padding: clamp(6px, 1.1vh, 10px) 10px;
  }

  /* The rail has height to spare now that the round fraction moved to the progress bar, so
     the information that IS here gets the room rather than the room going unused. Nothing
     new was added: these are the same four things (name, score, cards, streak), larger.
     A score is read from across a shared classroom table, which is the case that decides
     this size. */
  .vm-player-score { font-size: clamp(34px, 4.6vw, 60px); line-height: 1; }

  /* In a rail the name has height to spare and no width to waste, so it wraps instead of
     ellipsising. The portrait header is the opposite shape and keeps the single-line
     ellipsis — truncating someone's own name is a poor trade when there is room below it. */
  .vm-player-head { flex-wrap: wrap; row-gap: 4px; }
  .vm-player-name {
    white-space: normal;
    overflow: visible;
    overflow-wrap: anywhere;
    font-size: clamp(14px, 1.35vw, 19px);
  }

  /* The rest of the rail, scaled to match the score above it. The streak lives in the meta
     column rather than inside a player card, so it is targeted directly here. */
  .vm-player { gap: 6px; }
  .vm-meta-line { font-size: clamp(12px, 1.1vw, 15px); }
  .vm-badge { font-size: 11px; padding: 3px 8px; }
  .vm-streak { gap: 5px; }
  .vm-pip { width: clamp(16px, 1.5vw, 22px); height: clamp(16px, 1.5vw, 22px); }

  .vm-meta {
    flex: 0 0 auto;
    border-top: 1px solid var(--vm-border);
    border-bottom: 1px solid var(--vm-border);
    padding: clamp(6px, 1.2vh, 10px) 0;
  }

  /* ---- right rail: the two revealed words, stacked ---- */

  .vm-revealed {
    /* One column, two rows — the pair reads top to bottom beside the board rather than
       spanning under it. */
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: minmax(0, 1fr);
    align-content: center;
    min-height: 0;
  }

  .vm-revealed-slot { min-height: clamp(88px, 15vh, 150px); }
  .vm-revealed-hint { display: none; }

  /* Big enough to read across a room — this gets played on a projector — but the rail is
     narrower than the old full-width panel, so the ceiling comes down and the long-word
     rule does more of the work. "Understands" is the longest form in the dataset. */
  .vm-revealed-word { font-size: clamp(20px, 2.5vw, 40px); }
  .vm-revealed-word.is-long { font-size: clamp(15px, 1.7vw, 26px); }

  /* ---- the message, under the words ---- */

  .vm-status {
    align-self: start;
    text-align: center;
    min-height: 3.4em;
    margin-top: var(--vm-gap);
  }
}

/* Small screens: trim the chrome so the 8-row diamond keeps as much height as possible. */
@media (max-width: 430px) {
  :root { --vm-pad: 8px; --vm-gap: 8px; }
  .vm-revealed { min-height: clamp(64px, 12vh, 96px); }
  .vm-player-score { font-size: clamp(18px, 4.6vw, 26px); }
  .vm-status { min-height: 3.2em; }
}
