/* Copyright (c) 2024-2026 Kilotronic LLC. All rights reserved. */

/* Sticky-footer column: the content area (.container) flex-grows to fill the
   viewport and centers the active screen; the shared <site-footer> is pushed to
   the bottom. Using a column (not the old default-row flex) keeps the footer
   from floating beside the content mid-viewport on mobile Safari. 100dvh tracks
   Safari's dynamic toolbar so the footer pins to the real bottom. */
body {
  color: var(--text);
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.container {
  width: 100%;
  max-width: 400px;
  padding: 2rem 1.5rem;
  /* Fill remaining height (never shrink below content), and center the single
     active .screen vertically. When content exceeds the viewport the container
     grows to fit it, so nothing is clipped — the body simply scrolls. */
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* In-session screens (keyed on the branded .session-header, not screen ids)
   anchor to the top: a short screen like the lobby otherwise floats its
   header mid-viewport, which reads as broken next to the submitted screen. */
.container:has(.screen.active .session-header) {
  justify-content: flex-start;
}
h1 {
  font-family: var(--font-display);
  font-size: 1.5rem;
  text-align: center;
  margin-bottom: 1.5rem;
}
.screen {
  display: none;
}
.screen.active {
  display: block;
}
label {
  display: block;
  font-size: 0.9rem;
  margin-bottom: 0.25rem;
  color: var(--text-dim);
}
input[type="text"] {
  width: 100%;
  padding: 0.85rem;
  font-size: 1.1rem;
  border: 2px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  color: var(--text);
  margin-bottom: 1rem;
}
input[type="text"]:focus {
  outline: none;
  border-color: var(--accent);
}
/* Base <button> reset: only the non-visual behavior every play.html button
   needs, regardless of family (icon-btn, composer-add-btn, .btn, etc.) — cursor,
   font inheritance (so buttons pick up the ambient body face instead of the
   browser's native UI-button font), and the appearance/border reset. Skin
   (fill, padding, radius, shape) is each family's own job now: the CTAs
   migrated onto shared.css's `.btn`/`.btn--primary` system (ui-style backlog
   #1 — one primary-button implementation instead of three; #4 — one
   disabled model, `.btn`'s opacity-dim, not a background swap here). */
button {
  font-family: inherit;
  font-size: inherit;
  border: none;
  cursor: pointer;
}
button:disabled {
  cursor: not-allowed;
}
/* Primary CTAs stack full-width in their single-column screens — width is
   layout, not skin, so it stays a play.css rule rather than moving into
   `.btn`. `.container` already caps to phone width (max-width: 400px), so
   plain full width needs no additional cap here. */
#code-btn,
#join-btn,
#prompt-btn {
  width: 100%;
}
.error {
  color: var(--c-negative);
  font-size: 0.9rem;
  margin-bottom: 0.75rem;
  min-height: 1.4em;
}
.error--working {
  color: var(--text-dim);
}
.instructions {
  color: var(--text-dim);
  font-size: 0.9rem;
  margin-bottom: 1rem;
  text-align: center;
}
.done-msg {
  text-align: center;
  font-size: 1.15rem;
  line-height: 1.6;
  margin-top: 1rem;
}
.join-intro {
  margin: 0 0 1.25rem;
  line-height: 1.45;
  opacity: 0.7;
  font-size: 0.95rem;
}
/* .code-part-tail::before (the thin space) lives in shared.css. The old
   .code-display hero went with the set-your-name re-cut: the party code is
   header furniture now (.session-header-code), not the subject of the screen. */
.review-list {
  list-style: none;
  padding: 0;
  margin: 0 0 0.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.lobby-count {
  color: var(--text-dim);
  font-size: 0.95rem;
  margin-bottom: 1rem;
  text-align: center;
}
#gameplay-state {
  text-align: center;
  margin-bottom: 1rem;
}
.game-over-msg {
  text-align: center;
  font-size: 1.3rem;
  font-weight: 700;
  margin-top: 1rem;
  color: var(--accent);
}
#gameplay-round {
  text-align: center;
  font-size: 0.85rem;
  margin-bottom: 0.75rem;
}
#gameplay-feedback {
  text-align: center;
  font-size: 1rem;
  padding: 0.5rem;
  border-radius: var(--radius-sm);
  margin-bottom: 0.75rem;
}
#gameplay-content {
  text-align: center;
  font-size: 1.15rem;
  padding: 1rem;
  margin-bottom: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}
/* A string-valued card is the hero prompt — RegionRenderers' card treatment
   marks it `.prompt-hero`, the same treatment that renders a deck card's panel
   (the two are one thing: see the --font-card token, "content face for deck
   cards + hero prompts"). The HOST styles it through `#phone-headline.is-prompt`
   (card face + accent glow) and fits it to the stage; the phone has no fit, so
   without this it inherits #gameplay-content's 1.15rem body face and the prompt
   reads as ordinary text rather than as the content it is. Sized like
   `.card-content-text` in shared.css — the object-valued sibling of this same
   branch — which lands at ~24px on a phone: reading size, not room size. The
   host owns the room's display; the phone is a peripheral.

   Deliberately player-scoped rather than a bare `.prompt-hero` rule in
   shared.css: FitText writes font-size on the `.headline-fit` WRAPPER and the
   hero inherits it, so a font-size on the child would override the host's fit
   and pin every hero prompt to this clamp. */
#gameplay-content .prompt-hero {
  font-family: var(--font-card);
  font-weight: var(--font-card-weight);
  font-size: clamp(1.5rem, 6vw, 3rem);
}
.gameplay-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0.5rem;
}
.gameplay-header h1 {
  margin-bottom: 0;
  flex: 1;
  text-align: center;
}
#gameplay-timer {
  text-align: center;
  margin-bottom: 0.75rem;
}
#gameplay-counter {
  text-align: center;
  margin: 1.5rem 0;
}
#gameplay-counter .counter-value {
  font-size: 4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--clr-scored);
}
#gameplay-counter .counter-label {
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-top: 0.25rem;
}
/* Turn stats: the engine's projected status/detail chips ("Score: 6",
   "4 left") during an active turn — a passthrough (GC.renderTurnStatsHtml),
   so each chip is one already-composed string, not a split number+label
   (docs/todo.md, 2026-08-04). */
#gameplay-turninfo {
  display: flex;
  justify-content: center;
  gap: 1.25rem;
  margin: 0.75rem 0;
}
#gameplay-turninfo .turnstat {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--clr-scored);
}
#gameplay-dice {
  text-align: center;
  margin: 1.5rem 0;
}
#gameplay-dice .dice-value {
  font-size: 4rem;
  font-weight: 700;
  line-height: 1;
  color: var(--clr-scored);
}
body {
  /* Inter, via the shared token — keeps the player app on the same two-face
     system as the host (was a local-only Atkinson that never loaded on most
     phones, falling back to system-ui). */
  font-family: var(--font);
}
/* `.sr-only` (visually-hidden) lives in shared.css, which play.html loads. */
/* Branded in-session header: Spraoi wordmark + join code (left) and player name (right). */
.session-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 1rem;
}
.session-header-brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1;
}
.brand-wordmark {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--text);
}
.session-header-code {
  margin-top: 0.15rem;
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--accent);
}
.session-header-code .code-part {
  letter-spacing: 0.12em;
}
.session-header-player {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  text-align: right;
}
.icon-btn {
  margin: 0;
  font-size: 1.1rem;
  font-weight: 600;
  background: var(--bg-card);
  border: 1px solid var(--border);
  color: var(--text);
  cursor: pointer;
}
.prompt-list {
  list-style: none;
  padding: 0;
  margin: 0 0 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
/* Bottom-edge mirror of the composer bar's own sticky mechanism: while
   there's more list below the fold, this stays pinned to the bottom of the
   viewport (position: sticky; bottom: 0 — the same recipe the composer uses
   pinned to the top) fading the last row toward the page background, a
   "keep scrolling" cue.
   Once the list has actually scrolled past (the real #prompt-error/#prompt-btn
   come into view below it), it scrolls away with the rest of the list's
   trailing content instead of sitting stuck over the real controls.
   aria-hidden + pointer-events: none — decorative only, never a tap target.
   1.5rem is about half a .prompt-row's own height (min-height: 44px below)
   — enough to read as a fade over the last row's tail, not a hard cut. */
.prompt-list-scroll-fade {
  position: sticky;
  bottom: 0;
  height: 1.5rem;
  margin-top: -1.5rem;
  background: linear-gradient(to bottom, transparent, var(--bg-2));
  pointer-events: none;
}
/* Impostor lobby scroll/add-row/fade live in shared.css
   (.impostor-roster-list.is-lobby-scroll, .impostor-lobby-add-row,
   .impostor-roster-scroll-fade). Player-only bits below. */
#lobby-screen #lobby-add-error {
  min-height: 1.2em;
  color: var(--c-danger-text, var(--text-dim));
  font-size: 0.85rem;
  margin: 0 0 8px;
}
/* Bottom runway so the fixed QR card does not cover Add / roster. */
#lobby-screen:has(#lobby-partycode:not([hidden])) {
  padding-bottom: 340px;
}
.prompt-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.6rem;
  min-height: 44px;
  /* The sticky composer pins to the TOP of the screen (composer-top
     restructure, 2026-08-04), so a scroll-into-view targeting a row —
     directly, e.g. commitActiveInput's scrollIntoView() on the newly added
     last row, or via beginRowEdit/focusAfterDelete explicitly scrolling the
     row itself before focusing a descendant control (play.js) — must clear
     its real edge, or the row lands right under it.
     --composer-h (set by wireComposerHeightObserver() in play.js, a
     ResizeObserver on the composer bar) is its actual current rendered
     height: it varies with whether #prompt-instructions/#char-counter are
     showing and with the safe-area inset, so a guessed constant can
     undershoot (e.g. notched Safari with both optional rows visible). The
     5.5rem fallback only matters for the (today, effectively none) browsers
     with no ResizeObserver, or before the first measurement runs; +0.5rem is
     a little breathing room past the bar's exact edge. */
  scroll-margin-top: calc(var(--composer-h, 5.5rem) + 0.5rem);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
}
/* `anywhere`, not `break-word`: only `anywhere` lets a soft-wrap opportunity
   shrink the min-content size, and this is a `flex: 1` item with no
   `min-width: 0`, so with `break-word` a long unbroken player-typed string
   (a pasted URL, a keyboard mash) floors the row's width and pushes it past a
   narrow viewport. This is what the legacy `word-break: break-word` it
   replaced actually computed to. */
.prompt-row .row-text {
  flex: 1;
  font-size: 1rem;
  overflow-wrap: anywhere;
}
/* Tap the word itself to edit (touch shortcut alongside the ✎ button). */
.prompt-row .row-text-tappable {
  cursor: pointer;
}
.prompt-row.is-duplicate {
  border-color: var(--color-accent-2);
}
.prompt-row .dup-flag {
  font-size: 0.8rem;
  color: var(--color-accent-2);
}
.prompt-row.editing {
  flex-wrap: wrap;
}
.prompt-row input {
  flex: 1;
  margin-bottom: 0;
  font-size: 16px;
}
/* The placeholder treatment (dimmed + italic) is app-wide — see the
   ::placeholder rule in shared.css.

   ...with one carve-out. #code-input's placeholder is a format mask, not sample
   prose: a run of identical glyphs skewed by the synthetic oblique smears into
   an unreadable blur, and a mask has no voice to italicise anyway. It keeps the
   shared dim colour — that is what tells a hint apart from a value, and this
   field prefills a real code into the value (prefillCodeInput in play.js), so
   the empty and prefilled states must stay distinguishable. */
#code-input::placeholder {
  font-style: normal;
}
.char-counter {
  margin: 0.15rem 0 0.4rem;
  font-size: 0.8rem;
  text-align: right;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.char-counter.char-counter--limit {
  color: var(--c-negative);
}
/* -- Minimal composer (prompt screen) -------------------------------- */
/* One job per screen: the composer pins to the top, the list scrolls under
   it — moved from sticky-bottom in the composer-top restructure (2026-08-04)
   so the input survives a long list's scroll the same way the Impostor
   lobby's "Add a player" input will. */
.composer-bar {
  position: sticky;
  top: 0;
  /* Clear the top safe area (status bar / notch) now that this bar pins to
     the top rather than the bottom, where it used to clear the home
     indicator instead. */
  padding: calc(0.5rem + env(safe-area-inset-top)) 0 0.5rem;
  background: var(--bg-2);
  /* #prompt-list's rows come AFTER this bar in source order now, so without
     an explicit stacking context they would paint over it as they scroll
     underneath — z-index keeps the composer on top regardless of paint
     order. #host-start-banner/#connection-banner (shared.css, z-index
     10/100) deliberately still win over this: both are transient, fixed-top
     alerts (auto-dismiss or connectivity-scoped), and having them briefly
     cover the composer while shown is the SAME pre-existing trade-off every
     other in-session screen already makes for its own top content — e.g.
     .session-header (the Spraoi wordmark/join code) on the review/lobby
     screens has never had special accommodation for these banners either.
     Moving the composer to the top just adds it to that same list; it is
     not a novel regression, and is not being specially fixed here. Flag for
     the real-app iOS pass: confirm the transient overlap (reconnect blip,
     or a host-start mid-edit) still reads fine with the input as the thing
     underneath, not just static text. */
  z-index: 1;
}
/* The spec's authored submission_prompt (session/aggregate.py's
   PromptRequirements.instructions), one line above the input — restored
   2026-08-04 (owner call 2026-08-02). :empty hides it for the (common) case
   of a spec with no submission_prompt configured, same pattern as
   .char-counter:empty below. Scoped to the id, NOT the shared .instructions
   class the HTML element also carries (color/text-align/margin-bottom,
   #gameplay-state's only other consumer) — an earlier revision redefined
   .instructions itself, which silently dropped #gameplay-state's font-size
   from 0.9rem to 0.85rem (claude-review finding on PR #1880). The id's
   higher specificity overrides just this element's font-size/margin-bottom,
   leaving .instructions and its other consumer untouched. */
#prompt-instructions {
  font-size: 0.85rem;
  margin-bottom: 0.4rem;
}
#prompt-instructions:empty {
  display: none;
}
.composer-row {
  display: flex;
  gap: 0.5rem;
  /* Containing block for .composer-hint. */
  position: relative;
}
/* The composer's hint is a real element rather than the field's `placeholder`
   — a placeholder has no box of its own, and Safari mis-invalidates it when the
   field is cleared inside the sticky bar above, painting it clipped to the
   removed text's rect. See refreshComposerEmptyState() in play.js.
   Parked on the field's text origin: the input's 2px border + 0.85rem padding
   (see the base input[type="text"] rule), at the composer's own font size. */
.composer-hint {
  position: absolute;
  left: calc(0.85rem + 2px);
  top: 50%;
  transform: translateY(-50%);
  font-size: 1.125rem;
  font-style: italic;
  color: var(--text-dim);
  /* Never swallow a tap meant for the field underneath. */
  pointer-events: none;
}
.composer-row .active-input {
  flex: 1;
  min-height: 48px;
  font-size: 1.125rem;
  /* The base input margin-bottom (1rem) grows the flex line's cross size,
     so align-self:center parks the add button 8px below the input's visual
     center. The composer-bar padding already provides the spacing. */
  margin-bottom: 0;
}
.composer-add-btn {
  /* Geometry (target, corner, centring) comes from the glyph-button family in
     shared.css. The two things that stay here are load-bearing and specific to
     this button: a FIXED square rather than the family's min-* (min-height
     alone let it stretch to the flex row's height -> a 48-wide, ~68-tall oval,
     and the fixed width escapes the base `button` width so it can't claim the
     row and squeeze #active-input to a sliver — an e2e regression where
     Chromium mis-truncates a long paste into a near-zero-width input), plus
     align-self:center so it doesn't stretch now that height is fixed. Sized to
     the family's 44px rather than its old 48px (2026-08-01 owner decision: one
     size for every glyph button). */
  flex: none;
  width: 44px;
  height: 44px;
  align-self: center;
  /* Coral fill + dark glyph, matching the physical controller action caps
     (dark #14110f on accent, >=6.4:1) so "add" reads as an intentional
     micro-action instead of a UA-default gray button. */
  background: var(--accent);
  color: #14110f;
}
/* The glyph is an inline SVG (see play.html) so it centers geometrically;
   size it like the Messages send arrow (~45% of the circle). */
.composer-add-btn svg {
  display: block;
  width: 22px;
  height: 22px;
}
.composer-add-btn:active {
  background: var(--accent-active);
}
/* Inert look while there's nothing to add (still clickable; blank no-ops). */
.composer-add-btn.is-empty {
  opacity: 0.4;
}
/* Always-visible per-row delete */
.prompt-row .row-delete {
  flex-shrink: 0;
}
/* Title row: label on the left, a green ✓ revealed to its right once the
   player has added the suggested number of names (replaces the ●○ dot meter). */
.prompt-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.quota-check {
  color: var(--c-positive);
  font-weight: 700;
  font-size: 0.9em;
  line-height: 1;
  animation: quota-pop 0.25s ease-out;
}
@keyframes quota-pop {
  from {
    opacity: 0;
    transform: scale(0.6);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
.done-sub {
  text-align: center;
  color: var(--text-dim);
  font-size: 0.95rem;
  margin-bottom: 1rem;
}
.done-status {
  text-align: center;
  font-size: 0.95rem;
  color: var(--text-dim);
  margin: 0.75rem 0;
  min-height: 1.4em;
}
.done-hero {
  text-align: center;
  margin: 0.75rem 0 1.25rem;
}
.done-check {
  display: inline-flex;
  width: 2.4rem;
  height: 2.4rem;
  color: var(--clr-scored);
  animation: quota-pop 0.25s ease-out;
}
.done-check svg {
  width: 100%;
  height: 100%;
}
/* The done screen's card rows mirror .prompt-row so the list the player just
   built doesn't change visual language after "I'm done". */
.review-row-btn,
.review-add-btn {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.6rem;
  min-height: 44px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  color: var(--text);
  font-size: 1rem;
  text-align: left;
  cursor: pointer;
}
/* `anywhere` for the same reason as `.prompt-row .row-text` above. */
.review-row-btn .row-text {
  flex: 1;
  overflow-wrap: anywhere;
}
.review-row-btn .row-edit-glyph {
  color: var(--text-dim);
  font-size: 1.1rem;
}
.review-add-btn {
  background: none;
  border-style: dashed;
  color: var(--text-dim);
  margin-bottom: 1rem;
}
.done-ready-count {
  margin-top: 0.25rem;
}
.host-start-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 10;
  background: var(--accent);
  color: var(--bg-2);
  text-align: center;
  padding: 0.6rem 2.5rem 0.6rem 1rem;
  font-weight: 600;
}
.snackbar {
  position: fixed;
  left: 50%;
  /* Clear the <site-footer> (wraps to ~75px at phone widths) so the undo
     affordance never covers the legal links beneath it. */
  bottom: calc(5.5rem + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: var(--surface);
  color: var(--text);
  padding: 0.6rem 1rem;
  border-radius: var(--radius-md);
  max-width: 90%;
}
.snackbar-action {
  width: auto;
  min-height: 44px;
  margin: 0;
  padding: 0 0.75rem;
  /* font-size + border-radius formerly came from the base `button{}` reset;
     now that the reset is skin-free, this family needs its own. */
  font-size: 1.1rem;
  border-radius: var(--radius-md);
  background: none;
  border: 1px solid var(--accent);
  color: var(--accent);
  font-weight: 600;
}

/* No reduced-motion override: quota-pop's `to` keyframe (opacity:1,
   scale(1), the base state) matches shared.css's universal instant-finish
   rule regardless. */
/* Shared site footer — pinned at the bottom of the sticky-footer column (the
   container above flex-grows). flex-shrink:0 keeps it full-height; width:100%
   so its centered text spans the viewport rather than hugging one edge. */
site-footer {
  flex: 0 0 auto;
  width: 100%;
  padding: 1.5rem 1rem 2rem;
}

/* Destructive-exit gate + profile dialog — a deliberately more prominent card
   than the standard .overlay-dialog chrome (surface --bg-card, border --border,
   0.75rem radius, 1.5rem padding); see the leave-confirm design note. */
#leave-confirm,
#profile-dialog {
  z-index: 100;
  padding: 1.5rem;
}
.overlay-dialog--prominent {
  max-width: 22rem;
  width: 100%;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 0.75rem;
  padding: 1.5rem;
  text-align: center;
}
/* `.dialog-actions` (shared.css) supplies display/gap/justify-content; this
   overrides gap and adds the margin-top the player card wants. With `flex: 1`
   children below, the shared `justify-content: flex-end` is inert. */
.leave-confirm-actions {
  margin-top: 1.25rem;
  gap: 0.75rem;
}
/* Fill/radius/press now come from `.btn`/`.btn--danger`/`.btn--surface` (the
   standard-button family, see play.html) — this stays a pure layout rule
   (equal-width split), not skin. */
.leave-confirm-actions button {
  flex: 1;
}
/* .player-menu-btn (lobby, gameplay, review headers) carries only the shared
   .menu-trigger skin now (shared.css, spec 2026-07-30); the bordered-pill
   look is retired. The class name stays in markup — play.js keys off it for
   aria-expanded wiring — but no CSS rule targets it anymore. */
/* In-game menu overlay — rides the shared `.overlay-backdrop`/`.overlay-dialog`
   classes directly (OverlayFactory, see play.js), mirroring the host
   #menu-overlay/#menu-inner pattern so both read as one family. Only the
   per-id deltas (z-index, padding — display comes from `.overlay-backdrop`'s
   shared `[open]` rule) live here. */
#player-menu-overlay {
  z-index: 100;
  padding: 1.5rem;
}
.player-menu-card {
  max-width: 22rem;
  width: 100%;
}
/* Title row: label left, ✕ right — matches host #menu-inner h3 layout.
   Skin (font-size/color/margin) is the shared `.overlay-dialog h3,
   .player-menu-title-row` rule in shared.css; this keeps only the layout. */
.player-menu-title-row {
  display: block;
}
/* ✕ close — always visible; the player menu never had a touch-only gate, and
   the host's was deleted with the menu-strategy spec 2026-07-30.
   .overlay-close + :hover live in shared.css (host + player share them). */
/* Menu item rows — each is a full-width tappable row (≥44px). */
.player-menu-items {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Player metric deltas over the shared .menu-item (rem for OS font scaling —
   see the px-vs-rem note in docs/ui-style.md) + touch/keyboard focus state
   (host drives .focused via the d-pad cursor instead). */
.player-menu-items .menu-item {
  padding: 0.6rem 0.75rem;
  font-size: 0.9rem;
  text-align: left;
  width: 100%;
  margin: 0;
}
.menu-item:focus {
  background: var(--bg-1);
  border-color: var(--accent);
  outline: none;
}
/* Wake toggle uses .active for the "lock on" visual accent. */
.menu-item.wake-toggle.active {
  border-color: var(--clr-scored);
  color: var(--clr-scored);
}
/* Leave — destructive row, red tint. */
.player-menu-leave {
  color: var(--c-negative);
  border-color: var(--c-negative);
}
.player-menu-leave:hover,
.player-menu-leave:focus {
  background: var(--c-danger-fill);
  color: #fff;
}
/* The persistent corner Sign out and its per-screen data-signout opt-in were
   retired on 2026-08-03: the set-your-name screen (its only consumer) now wears
   the branded .session-header, whose menu is the one exit affordance on every
   player screen. The opt-in existed so a NEW screen failed safe rather than
   silently showing a destructive control (PR #1488 defect class); that property
   is now structural — openPlayerMenu shows the Sign out row only PRE-join, so
   no post-join screen can offer it however it grows its chrome. */

/* Share-device note on the review screen — inline sign-out link. */
.share-note {
  margin-top: 1.5rem;
  text-align: center;
  font-size: 0.82rem;
  color: var(--text-muted);
}
/* Inline button that looks like a hyperlink. */
/* Type + colour come from the shared text-link family. */
.inline-btn {
  text-decoration: underline;
  margin: 0 0.1em;
}
/* Reduced prominence, not reduced legibility (HIG / seniors bar): the prefix
   stays muted, the link itself is underlined body-color text. */
.share-note .inline-btn {
  color: var(--text);
  text-decoration: underline;
  font-size: 0.95rem;
}
