/* ─────────────────────────────────────────────────────────────────────
   /games/shared/mp.css — shared multiplayer styles for /games/* pages.

   PHASE 2 SKELETON.  This file currently ships the section structure
   (banner comments + a tiny handful of rules that don't conflict with
   anything CE already has inline) but NOT the full rule set.  The
   ~136 `.mp-*` selectors currently inline in `/games/crazy-eights/
   index.html` (lines 445-1700 approximately) migrate into this file
   during Phase 3 of the `checkers-add-multiplayer.md` change.

   When Phase 3 runs:
   - All `.mp-*` rules under the `Multiplayer` band of CE's <style>
     get cut from CE and pasted here, into the right section below.
   - The mode-picker `.ce-mode-card` / `.ce-mode-bar-switch` rules get
     renamed to `.mp-mode-card` / `.mp-mode-bar-switch` and pasted
     into the "Mode picker" section (per Q13 — markup stays inline
     per page; visual treatment lives here).
   - The Catch! banner CSS (`.mp-lc-banner` etc.) STAYS in CE
     (game-specific UI per Q3/Q4-adjacent inventory tagging).

   This file is loaded via:
     <link rel="stylesheet" href="/games/shared/mp.css?v=YYYYMMDD">

   The `?v=YYYYMMDD` is the cache-busting handle (per Q10 of the
   change definition).  Bump the version with
   `work/scripts/mp-version-bump/bump.py` when this file changes.

   Section order below mirrors `work/docs/games-mp-inventory.md`'s
   "CSS" subsection grouping so the inventory stays a navigational
   tool for both files.
   ───────────────────────────────────────────────────────────────── */

/* ═══════════════════════════════════════════════════════════════════
   1. Presence dots
   Used on seat rows + the in-game room strip to signal seat status.
   Variants: .is-active (connected human), .is-recon (reconnecting),
   .is-ai (AI standin or host-added AI), .is-host (the host).

   Pure CSS, no `@apply` — moves verbatim from CE's inline <style>
   block (lines 453-464 pre-migration).  Phase 3 of the
   `checkers-add-multiplayer.md` extract.
   ═══════════════════════════════════════════════════════════════════ */

.mp-presence-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: rgba(255,255,255,0.30);
  border: 1px solid rgba(255,255,255,0.10);
}
.mp-presence-dot.is-active { background: #10b981; }
.mp-presence-dot.is-recon  { background: #f59e0b; }
.mp-presence-dot.is-ai     { background: #94a3b8; }
.mp-presence-dot.is-host   { background: #60a5fa; }

/* ═══════════════════════════════════════════════════════════════════
   2. Lobby container + state machine
   `.mp-lobby` is the outer <section>; `.mp-lobby-state[data-mp-state]`
   are the per-state child panels (idle / host / join / spectating /
   ingame).  Idle sub-states are layered via `[data-active-sub]` on
   the same element.  Tailwind `@apply` directives expanded inline.
   ═══════════════════════════════════════════════════════════════════ */

.mp-lobby {
  /* @apply bg-white border border-rule rounded-[10px] px-4 sm:px-5 py-4 mb-3; */
  background: #ffffff;
  border: 1px solid #e4e7ee;
  border-radius: 10px;
  padding: 1rem;
  margin-bottom: 0.75rem;
}
@media (min-width: 640px) {
  .mp-lobby { padding-left: 1.25rem; padding-right: 1.25rem; }
}
.mp-lobby[hidden] { display: none; }
.mp-lobby-state { display: none; }
.mp-lobby-state.is-active { display: block; }
/* Idle panel: the CARD (white background + border) fills its parent
   width so it visually matches the surrounding hero / controls /
   board cards on each consumer page.  The form CONTENTS inside the
   card are capped at 460px centered — that's a "narrow column of
   form widgets inside a full-width card" pattern, which keeps the
   form itself comfortable to scan while letting the card's border
   align with the page's other cards. */
@media (min-width: 560px) {
  .mp-lobby-state[data-mp-state="idle"] > * {
    max-width: 460px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   3. Idle state — buttons, help disclosure
   Sub-modes (cache-empty, cache-populated, change-code, locked,
   banned, connecting) are layered via `[data-active-sub]`.
   The per-sub visibility selectors live in section 3b below.
   ═══════════════════════════════════════════════════════════════════ */

.mp-idle-buttons {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 360px;
  margin: 8px auto 6px;
}
@media (min-width: 520px) {
  .mp-idle-buttons { flex-direction: row; }
  .mp-idle-buttons > button { flex: 1; }
}
.mp-idle-help {
  /* @apply text-[0.8125rem] text-mid mt-3; */
  font-size: 0.8125rem;
  color: #4a5168;
  margin-top: 0.75rem;
}
.mp-idle-help summary {
  /* @apply cursor-pointer font-semibold text-ink; */
  cursor: pointer;
  font-weight: 600;
  color: #1a1f2e;
}

/* ═══════════════════════════════════════════════════════════════════
   4. Host state — room code display, share row, QR
   ═══════════════════════════════════════════════════════════════════ */

.mp-room-code {
  /* @apply font-mono font-bold text-ink text-center tracking-[0.18em] select-all; */
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-weight: 700;
  color: #1a1f2e;
  text-align: center;
  letter-spacing: 0.18em;
  user-select: all;
  font-size: 2rem;
  line-height: 1.1;
  margin: 4px 0 8px;
}
.mp-share-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  justify-content: center;
  margin-bottom: 10px;
}
.mp-share-link {
  /* @apply font-mono text-[0.8125rem] text-mid bg-bgsoft border border-rule rounded-md px-2 py-1 select-all; */
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 0.8125rem;
  color: #4a5168;
  background: #f8f9fc;
  border: 1px solid #e4e7ee;
  border-radius: 6px;
  padding: 0.25rem 0.5rem;
  user-select: all;
  max-width: 360px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mp-qr-wrap {
  display: flex;
  justify-content: center;
  margin: 8px 0 12px;
}
.mp-qr-wrap svg {
  width: 140px;
  height: 140px;
  background: #ffffff;
  padding: 6px;
  border: 1px solid #e4e7ee;
  border-radius: 6px;
}
@media (max-width: 640px) {
  .mp-qr-wrap svg { width: 120px; height: 120px; }
}

/* ═══════════════════════════════════════════════════════════════════
   5. Seat list — rows, dots, names, badges, actions
   ═══════════════════════════════════════════════════════════════════ */

.mp-seat-list {
  /* @apply border border-rule rounded-md; */
  border: 1px solid #e4e7ee;
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: 10px 0;
}
.mp-seat-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid #e4e7ee;
  flex-wrap: wrap;
}
.mp-seat-row:last-child { border-bottom: none; }
.mp-seat-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: #cbd2e0;
  border: 1px solid rgba(0,0,0,0.06);
}
.mp-seat-dot.is-active   { background: #10b981; }
.mp-seat-dot.is-recon    { background: #f59e0b; }
.mp-seat-dot.is-ai       { background: #8891a8; }
.mp-seat-dot.is-host     { background: #2f6ef5; }
.mp-seat-name {
  /* @apply text-[0.9375rem] text-ink font-semibold; */
  font-size: 0.9375rem;
  color: #1a1f2e;
  font-weight: 600;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mp-seat-badge {
  /* @apply text-[0.6875rem] font-bold tracking-wider uppercase px-1.5 py-0.5 rounded-md; */
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.125rem 0.375rem;
  border-radius: 6px;
  background: #e0e7ff;
  color: #1a4fc4;
}
.mp-seat-badge.is-ai { background: #e4e7ee; color: #4a5168; }
/* AFK-on-turn and long-offline modifiers on the seat-row badge. Both
   use a warning-amber palette to read as "needs attention" without
   shouting like a destructive red would. The is-offline variant is
   slightly darker / more saturated to signal a worse drop than the
   recoverable AFK-on-turn case. The matching banner under
   .mp-afk-banner uses the same hues. */
.mp-seat-badge.is-afk     { background: #fef3c7; color: #92400e; }
.mp-seat-badge.is-offline { background: #fee2e2; color: #991b1b; }
.mp-seat-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

/* ═══════════════════════════════════════════════════════════════════
   6. Host settings + deal-row + join-form + input + error
   ═══════════════════════════════════════════════════════════════════ */

.mp-host-settings {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  margin: 10px 0;
}
.mp-host-settings .mp-setting-label {
  /* @apply text-[0.6875rem] font-bold tracking-wider uppercase text-muted mr-1; */
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #8891a8;
  margin-right: 0.25rem;
}
.mp-deal-row {
  display: flex;
  justify-content: center;
  margin-top: 8px;
}
.mp-join-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 360px;
  margin: 4px auto;
}
.mp-input {
  /* @apply text-[0.9375rem] text-ink bg-white border border-rule rounded-md px-3 py-2; */
  font-size: 0.9375rem;
  color: #1a1f2e;
  background: #ffffff;
  border: 1px solid #e4e7ee;
  border-radius: 6px;
  padding: 0.5rem 0.75rem;
  font-family: inherit;
  outline: none;
  transition: border-color 120ms;
}
.mp-input:focus { border-color: #2f6ef5; }
.mp-input.is-roomcode {
  /* @apply font-mono uppercase tracking-[0.22em] text-center text-[1.1rem] font-bold; */
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  text-align: center;
  font-size: 1.1rem;
  font-weight: 700;
}
.mp-error {
  /* @apply text-[0.8125rem] text-rose-700 bg-rose-50 border border-rose-200 rounded-md px-3 py-2; */
  font-size: 0.8125rem;
  color: #be123c;
  background: #fff1f2;
  border: 1px solid #fecdd3;
  border-radius: 6px;
  padding: 0.5rem 0.75rem;
}

/* ═══════════════════════════════════════════════════════════════════
   7. Status strip — compact in-game room display
   ═══════════════════════════════════════════════════════════════════ */

.mp-status-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  padding: 6px 4px;
}
.mp-status-strip .mp-room-tag {
  /* @apply font-mono font-bold text-ink tracking-[0.18em] text-[0.875rem]; */
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-weight: 700;
  color: #1a1f2e;
  letter-spacing: 0.18em;
  font-size: 0.875rem;
}
.mp-status-strip .mp-players-line {
  /* @apply text-[0.8125rem] text-mid; */
  font-size: 0.8125rem;
  color: #4a5168;
  flex: 1 1 auto;
  min-width: 0;
}
.mp-status-strip .mp-end-room {
  /* @apply text-[0.75rem] text-muted underline cursor-pointer; */
  font-size: 0.75rem;
  color: #8891a8;
  text-decoration: underline;
  cursor: pointer;
}
.mp-status-strip .mp-end-room:hover {
  /* @apply text-ink; */
  color: #1a1f2e;
}
@media (max-width: 700px) {
  .mp-status-strip .mp-players-line { display: none; }
}
.mp-status-strip .mp-room-lock,
.mp-status-strip .mp-room-watch,
.mp-status-strip .mp-spec-badge {
  /* @apply text-[0.875rem]; */
  font-size: 0.875rem;
  line-height: 1;
}
.mp-status-strip .mp-room-lock:empty,
.mp-status-strip .mp-room-watch:empty { display: none; }
.mp-status-strip .mp-room-watch {
  /* @apply text-[0.75rem] text-mid font-semibold; */
  font-size: 0.75rem;
  color: #4a5168;
  font-weight: 600;
  cursor: pointer;
}
.mp-status-strip .mp-room-watch:hover {
  /* @apply text-ink; */
  color: #1a1f2e;
}

/* ═══════════════════════════════════════════════════════════════════
   7b. AFK / offline banner
   Shown in-game whenever a seat has been flagged AFK-on-turn (45s
   with no play) or long-offline (75s past the heartbeat drop). The
   shared/mp.js render path (`renderAfkBanner`) creates the element
   lazily and inserts it right below the in-game status strip; this
   block styles it. Amber base for AFK, red-tinted for offline — same
   palette as the seat-row badges so the two surfaces feel related.
   The host's Kick button lives inline on each banner row so the room
   can unstick the game from anywhere in the UI without expanding the
   (otherwise-hidden) lobby panel.
   ═══════════════════════════════════════════════════════════════════ */

/* The banner is `position: fixed` at the top of the viewport so it
   overlays the site header rather than pushing it down. The room's
   need-to-act signal is more important than the page chrome — when
   someone's AFK or offline, every peer should see the banner front and
   center the moment they look at the tab, even if they happened to be
   scrolled into a different region of the page. A `z-index` well above
   any in-page element keeps it on top; the solid background means the
   header underneath isn't visible through it. Width spans the full
   viewport so the message reads at any breakpoint. */
.mp-afk-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 14px;
  background: #fef3c7;
  border-bottom: 2px solid #fbbf24;
  font-size: 0.875rem;
  color: #78350f;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.mp-afk-banner[hidden] { display: none; }
.mp-afk-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* The offline variant (heartbeat-confirmed drop) reads more urgent
   than AFK-on-turn (player is still connected, just inattentive).
   The row tints the parent banner red-ish via a data attribute so
   a mixed banner with both an AFK and an offline row still reads
   AFK overall — the offline row carries the urgency itself. */
.mp-afk-row[data-kind="offline"] {
  background: #fee2e2;
  color: #7f1d1d;
  padding: 4px 6px;
  border-radius: 6px;
}
.mp-afk-icon {
  font-size: 1rem;
  line-height: 1;
}
.mp-afk-text {
  flex: 1 1 auto;
  min-width: 0;
}
.mp-afk-kick {
  flex: 0 0 auto;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: 6px;
  background: #b91c1c;
  color: #fff;
  border: none;
  cursor: pointer;
}
.mp-afk-kick:hover  { background: #991b1b; }
.mp-afk-kick:focus  { outline: 2px solid #fbbf24; outline-offset: 2px; }
.mp-afk-kick:active { transform: translateY(1px); }

/* ═══════════════════════════════════════════════════════════════════
   8. Open-room button (returning-visitor primary CTA)
   Gradient blue, two-line label stack ("Open ABCD" / "as Player"),
   full-width within the parent (the 460px idle panel).  This is the
   production-CE pattern.
   ═══════════════════════════════════════════════════════════════════ */

.mp-open-room-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  width: 100%;
  padding: 7px 18px;
  border-radius: 8px;
  background: linear-gradient(135deg, #2f6ef5 0%, #1a4fc4 100%);
  color: #ffffff;
  border: none;
  cursor: pointer;
  text-align: center;
  font-family: inherit;
  font-size: 0.9375rem;
  line-height: 1.3;
  box-shadow: 0 2px 8px rgba(47, 110, 245, 0.20);
  transition: transform 80ms ease-out, box-shadow 120ms ease-out;
}
.mp-open-room-btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 5px 14px rgba(47, 110, 245, 0.32);
}
.mp-open-room-btn:active { transform: translateY(0); }
.mp-open-room-line1 {
  font-weight: 700;
  letter-spacing: 0.02em;
}
.mp-open-room-line1 > span {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  letter-spacing: 0.12em;
  margin-left: 4px;
}
.mp-open-room-line2 {
  font-size: 0.8125rem;
  font-weight: 500;
  opacity: 0.92;
}
.mp-open-room-line2 > span { font-weight: 700; }

/* ═══════════════════════════════════════════════════════════════════
   3b. Idle sub-mode visibility + idle links + idle messages
   The six sub-modes (cache-empty / cache-populated / change-code /
   locked / banned / connecting) layer atop the idle state via a
   `[data-active-sub]` attribute on the idle panel. The "no-host"
   sub-state (and its data-reason="name-taken" overlay) was removed
   2026-05-16 — `openRoomByCode` auto-hosts empty rooms and
   name-taken rejections surface inline at the idle lobby instead.
   See `work/done/mp-auto-host-on-room-open.md`.
   ═══════════════════════════════════════════════════════════════════ */

.mp-idle-sub { display: none; }
[data-mp-state="idle"][data-active-sub="cache-empty"]      .mp-idle-sub[data-idle-sub="cache-empty"],
[data-mp-state="idle"][data-active-sub="cache-populated"] .mp-idle-sub[data-idle-sub="cache-populated"],
[data-mp-state="idle"][data-active-sub="change-code"]     .mp-idle-sub[data-idle-sub="change-code"],
[data-mp-state="idle"][data-active-sub="locked"]           .mp-idle-sub[data-idle-sub="locked"],
[data-mp-state="idle"][data-active-sub="banned"]           .mp-idle-sub[data-idle-sub="banned"],
[data-mp-state="idle"][data-active-sub="connecting"]       .mp-idle-sub[data-idle-sub="connecting"] {
  display: block;
}
.mp-idle-links {
  /* @apply text-[0.8125rem] text-mid text-center; */
  font-size: 0.8125rem;
  color: #4a5168;
  text-align: center;
}
.mp-link-sep {
  /* @apply text-muted mx-2; */
  color: #8891a8;
  margin-left: 0.5rem;
  margin-right: 0.5rem;
}
.mp-text-link {
  /* @apply text-accent underline cursor-pointer bg-transparent border-0 p-0; */
  color: #2f6ef5;
  text-decoration: underline;
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
}
.mp-text-link:hover {
  /* @apply text-accent2; */
  color: #1a4fc4;
}
.mp-idle-msg {
  /* @apply text-[0.9375rem] text-mid text-center mb-3; */
  font-size: 0.9375rem;
  color: #4a5168;
  text-align: center;
  margin-bottom: 0.75rem;
}
.mp-idle-msg > strong {
  /* @apply text-ink font-mono tracking-[0.16em]; */
  color: #1a1f2e;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  letter-spacing: 0.16em;
}

/* ═══════════════════════════════════════════════════════════════════
   6a. .tc-chip — shared chip-pill component (host-side seat-row
   "+Easy / +Medium / +Hard" buttons + lock-room chip).  CE has this
   as a Tailwind @apply component in its inline <style> block; for
   shared use across consumer pages (so checkers doesn't have to
   redefine it), the hand-expanded version lives here in mp.css.
   ═══════════════════════════════════════════════════════════════════ */

.tc-chip {
  /* @apply inline-flex items-center text-[0.75rem] font-semibold
           px-2.5 py-1 rounded-full border border-rule bg-white
           text-mid cursor-pointer transition-colors
           hover:border-accent; */
  display: inline-flex;
  align-items: center;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.25rem 0.625rem;
  border-radius: 9999px;
  border: 1px solid #e4e7ee;
  background: #ffffff;
  color: #4a5168;
  cursor: pointer;
  font-family: inherit;
  transition: border-color 120ms, background-color 120ms, color 120ms;
}
.tc-chip:hover { border-color: #2f6ef5; }
.tc-chip.is-active {
  /* @apply bg-ink text-white border-ink; */
  background: #1a1f2e;
  color: #ffffff;
  border-color: #1a1f2e;
}
.tc-chip:disabled {
  cursor: not-allowed;
  opacity: 0.40;
  filter: grayscale(0.85);
}
.tc-chip:disabled:hover { border-color: #e4e7ee; }
.tc-chip.is-active:disabled {
  cursor: not-allowed;
}

/* ═══════════════════════════════════════════════════════════════════
   6b. Lock-room chip (host moderation, in the host's settings row)
   Outlined when unlocked, filled accent when locked.  Built on
   top of .tc-chip (border + padding inherited).
   ═══════════════════════════════════════════════════════════════════ */

.mp-lock-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.mp-lock-chip[aria-pressed="true"] {
  /* @apply bg-accent text-white border-accent; */
  background: #2f6ef5;
  color: #ffffff;
  border-color: #2f6ef5;
}
.mp-lock-chip[aria-pressed="true"]:hover {
  /* @apply bg-accent2 border-accent2; */
  background: #1a4fc4;
  border-color: #1a4fc4;
}
.mp-lock-icon { font-size: 0.95rem; line-height: 1; }

/* ═══════════════════════════════════════════════════════════════════
   12. Ban list — disclosure, row, allow-back button
   ═══════════════════════════════════════════════════════════════════ */

.mp-banned-disclosure {
  /* @apply mt-3; */
  margin-top: 0.75rem;
}
.mp-banned-disclosure > summary {
  /* @apply text-[0.75rem] text-mid cursor-pointer select-none; */
  font-size: 0.75rem;
  color: #4a5168;
  cursor: pointer;
  user-select: none;
  list-style: none;
}
.mp-banned-disclosure > summary::-webkit-details-marker { display: none; }
.mp-banned-disclosure > summary::before {
  content: '▸';
  display: inline-block;
  margin-right: 6px;
  transition: transform 120ms;
  color: #8891a8;
}
.mp-banned-disclosure[open] > summary::before { transform: rotate(90deg); }
.mp-banned-list {
  /* @apply mt-2 text-[0.8125rem] text-mid; */
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: #4a5168;
}
.mp-banned-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 0;
  border-bottom: 1px solid #e4e7ee;
}
.mp-banned-row:last-child { border-bottom: none; }
.mp-banned-row .mp-banned-name {
  /* @apply font-semibold text-ink; */
  font-weight: 600;
  color: #1a1f2e;
}
.mp-banned-row .mp-banned-when {
  /* @apply text-[0.75rem] text-muted ml-2; */
  font-size: 0.75rem;
  color: #8891a8;
  margin-left: 0.5rem;
}
.mp-banned-row .mp-banned-allow {
  /* @apply text-[0.75rem] text-accent underline cursor-pointer bg-transparent border-0 p-0; */
  font-size: 0.75rem;
  color: #2f6ef5;
  text-decoration: underline;
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 0;
  font: inherit;
}

/* ═══════════════════════════════════════════════════════════════════
   12b. Row-menu popover (the `…` menu on each seat/spectator row in
   the host's lobby — Kick / Kick & ban actions)
   ═══════════════════════════════════════════════════════════════════ */

.mp-row-menu-btn {
  /* @apply bg-transparent border border-rule rounded-md text-mid cursor-pointer; */
  background: transparent;
  border: 1px solid #e4e7ee;
  border-radius: 6px;
  color: #4a5168;
  cursor: pointer;
  width: 32px;
  height: 32px;
  font-size: 1rem;
  line-height: 1;
  font-weight: 800;
  padding: 0;
}
.mp-row-menu-btn:hover {
  /* @apply border-accent text-ink; */
  border-color: #2f6ef5;
  color: #1a1f2e;
}
.mp-row-menu-pop {
  position: absolute;
  z-index: 60;
  background: #ffffff;
  border: 1px solid #e4e7ee;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(26, 31, 46, 0.18);
  padding: 6px;
  min-width: 160px;
}
.mp-row-menu-pop button {
  /* @apply block w-full text-left text-[0.875rem] text-ink cursor-pointer bg-transparent border-0; */
  display: block;
  width: 100%;
  text-align: left;
  font-size: 0.875rem;
  color: #1a1f2e;
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 8px 10px;
  border-radius: 6px;
}
.mp-row-menu-pop button:hover {
  /* @apply bg-bgsoft; */
  background: #f8f9fc;
}
.mp-row-menu-pop button.is-danger {
  /* @apply text-rose-700; */
  color: #be123c;
}

/* ═══════════════════════════════════════════════════════════════════
   11b. Spectator list (inside the host's lobby) — count, list, row,
   per-row glyph/name/seat-button, empty state
   ═══════════════════════════════════════════════════════════════════ */

.mp-spec-count {
  /* @apply text-muted font-normal; */
  color: #8891a8;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}
.mp-spec-list {
  /* @apply text-[0.875rem] text-mid; */
  font-size: 0.875rem;
  color: #4a5168;
}
.mp-spec-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid #e4e7ee;
  position: relative;
}
.mp-spec-row:last-child { border-bottom: none; }
.mp-spec-row .mp-spec-glyph {
  font-size: 0.95rem;
  line-height: 1;
  opacity: 0.85;
}
.mp-spec-row .mp-spec-name {
  /* @apply text-ink font-semibold flex-1; */
  color: #1a1f2e;
  font-weight: 600;
  flex: 1 1 0%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mp-spec-row .mp-spec-seat {
  /* @apply text-[0.75rem] text-accent border border-accent rounded-md px-2 py-1 cursor-pointer bg-transparent; */
  font-size: 0.75rem;
  color: #2f6ef5;
  border: 1px solid #2f6ef5;
  border-radius: 6px;
  padding: 0.25rem 0.5rem;
  cursor: pointer;
  background: transparent;
  font: inherit;
}
.mp-spec-row .mp-spec-seat:hover {
  /* @apply bg-accent text-white; */
  background: #2f6ef5;
  color: #ffffff;
}
.mp-spec-row .mp-spec-seat:disabled {
  /* @apply text-muted border-rule cursor-not-allowed bg-transparent; */
  color: #8891a8;
  border-color: #e4e7ee;
  cursor: not-allowed;
  background: transparent;
}
.mp-spec-empty {
  /* @apply text-[0.8125rem] text-muted italic; */
  font-size: 0.8125rem;
  color: #8891a8;
  font-style: italic;
}
/* ═══════════════════════════════════════════════════════════════════
   8b. Room tab — checkers Friends-mode-only consolidated seat + spectator
   roster (rendered by `renderRoomTab()` in mp.js into `#mp-room-seats` /
   `#mp-room-specs` / `#mp-room-spec-count`).  Three host-only
   affordances: Kick on filled opponent seat rows, + Easy / + Medium /
   + Hard on empty seat rows, + Seat them on spectator rows.  Visibility
   is JS-gated in the renderer (no action elements appended for non-
   hosts); the `body:not(.is-mp-host) .mp-room-actions { display: none }`
   rule at the bottom is belt-and-suspenders for any future render-bug
   that accidentally leaks host markup onto a joiner / spectator tab.
   ═══════════════════════════════════════════════════════════════════ */
.mp-room-section { margin-bottom: 1rem; }
.mp-room-section:last-child { margin-bottom: 0; }
.mp-room-label {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #8891a8;
  margin: 0 0 0.5rem 0;
}
/* Room tab uses real <table> elements so columns auto-align across
   rows — easier to scan than flex-row wrapping.  Cells are six wide
   on the seats table (presence | name | side | host | type | actions)
   and three wide on the spectators table (glyph | name | actions). */
.mp-room-table {
  width: 100%;
  border-collapse: collapse;
}
.mp-room-table tr {
  border-bottom: 1px solid #f3f4f8;
}
.mp-room-table tr:last-child {
  border-bottom: none;
}
.mp-room-table td {
  padding: 0.375rem 0.5rem;
  font-size: 0.9375rem;
  color: #1a1f2e;
  vertical-align: middle;
}
/* First cell sits flush against the section's left edge; last cell
   flush against the right. */
.mp-room-table td:first-child { padding-left: 0; }
.mp-room-table td:last-child  { padding-right: 0; }
/* Presence column: small fixed width for the dot. */
.mp-room-cell-presence {
  width: 1.25rem;
  text-align: center;
}
/* Name column expands to fill the remaining space so side/host/type
   pin to the right side of the table next to actions. */
.mp-room-cell-name {
  width: 100%;
}
/* Side / host / type cells stay nowrap so the badge doesn't break. */
.mp-room-cell-side,
.mp-room-cell-host,
.mp-room-cell-type {
  white-space: nowrap;
}
/* Actions column right-aligns its content. */
.mp-room-cell-actions {
  white-space: nowrap;
  text-align: right;
}
.mp-room-presence {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: #10b981;
}
.mp-room-presence.is-recon   { background: #f59e0b; }
.mp-room-presence.is-offline { background: #f43f5e; }
.mp-room-presence.is-ai      { background: #8891a8; }
.mp-room-presence.is-empty   { background: #e4e7ee; }
.mp-room-name {
  font-weight: 500;
}
.mp-room-name.is-empty {
  color: #8891a8;
  font-style: italic;
  font-weight: 400;
}
.mp-room-side {
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #8891a8;
}
.mp-room-badge {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 0.125rem 0.375rem;
  border-radius: 4px;
}
.mp-room-badge.is-host  { background: #2f6ef5; color: #ffffff; }
.mp-room-badge.is-ai    { background: #fef3c7; color: #78350f; }
.mp-room-badge.is-human { background: #e4e7ee; color: #4a5168; }
.mp-room-empty {
  font-size: 0.875rem;
  color: #8891a8;
  font-style: italic;
  margin: 0.25rem 0 0 0;
}
.mp-room-spec-name {
  font-weight: 500;
}
/* "you" indicator — small accent-coloured pill suffixed to the local
   visitor's name on their row (both seat and spectator branches).  The
   tabular Room layout makes inline name+suffix harder to scan than the
   lobby-card's `(you)` plain-text convention, so this is rendered as a
   visually-distinct pill instead — small uppercase, accent-on-white
   inside an accent-tinted background. */
.mp-room-you {
  display: inline-block;
  margin-left: 0.375rem;
  padding: 0.0625rem 0.375rem;
  border-radius: 4px;
  background: #dbe6fd;
  color: #1a4fc4;
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: middle;
}
.mp-room-eye {
  display: inline-block;
}
/* Host-only Kick + add-AI + Seat-them affordances.  The actions cell
   already right-aligns the wrapper; inside the wrapper we lay buttons
   out as an inline-flex row so multi-button cells (three add-AI chips)
   stay horizontally aligned.  Hover hues reinforce the semantic: rose
   for remove (Kick), emerald for add (Seat-them + add-AI). */
.mp-room-actions {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}
.mp-room-action {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.125rem 0.5rem;
  border-radius: 4px;
  border: 1px solid #e4e7ee;
  background: transparent;
  color: #4a5168;
  cursor: pointer;
  font-family: inherit;
  transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
}
.mp-room-action:hover:not(:disabled) {
  color: #1a1f2e;
  border-color: #4a5168;
}
.mp-room-action:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.mp-room-action-kick:hover:not(:disabled) {
  color: #b91c1c;
  border-color: #fda4af;
}
.mp-room-action-seat:hover:not(:disabled),
.mp-room-action-add-ai:hover:not(:disabled) {
  color: #047857;
  border-color: #6ee7b7;
}
/* Belt-and-suspenders: hide host-only action elements on non-host
   tabs.  The renderer already skips appending these for non-hosts;
   this rule catches any future render-bug that accidentally leaks. */
body:not(.is-mp-host) .mp-room-actions { display: none; }

/* Chat-history spectator marker glyph (👀 prefix on spectator chat rows). */
.mp-chat-log-spec {
  /* @apply text-muted mr-1; */
  color: #8891a8;
  margin-right: 0.25rem;
  font-size: 0.85em;
}

/* ═══════════════════════════════════════════════════════════════════
   9. Chat block — log, row, input, send button + on-table bubbles +
   bubble float animation
   Lives inside the Chat tab on consumer pages that opt into it via
   `lobbyConfig.chatTab: true`.  The on-table bubble (.mp-chat-bubble)
   floats above each player's seat for ~6.5s when a chat message
   lands; spectator chat lands in the history-log only (no bubble).
   ═══════════════════════════════════════════════════════════════════ */

/* Solo mode hides the chat block; MP mode shows it.  Body-class
   gating is on `body.mp-active`. */
.mp-chat-block { display: none; }
body.mp-active .mp-chat-block { display: block; }
body.mp-active #chat-solo-placeholder { display: none; }

/* Input row — text input + Send button */
.mp-chat-row {
  display: none;
  flex-wrap: nowrap;
  gap: 6px;
  justify-content: center;
  align-items: center;
  margin-top: 8px;
  padding: 0 8px;
}
.mp-chat-row.is-on { display: flex; }
.mp-chat-input {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 320px;
  padding: 8px 14px;
  border: 1px solid;
  border-color: rgb(228 231 238);
  border-radius: 999px;
  font-size: 0.9rem;
  background: white;
  outline: none;
}
.mp-chat-input:focus {
  border-color: rgb(47 110 245);
  box-shadow: 0 0 0 3px rgba(47, 110, 245, 0.15);
}
.mp-chat-send {
  flex: 0 0 auto;
  padding: 8px 18px;
  border: none;
  border-radius: 999px;
  background: rgb(47 110 245);
  color: white;
  font-weight: 700;
  font-size: 0.875rem;
  cursor: pointer;
}
.mp-chat-send:hover { background: rgb(26 79 196); }
.mp-chat-send:disabled { opacity: 0.5; cursor: not-allowed; }

/* On-table bubble — pops above the sender's seat for ~6.5s.
   Spectator chat skips this and renders to the history log only. */
.mp-chat-bubble {
  position: fixed;
  z-index: 250;
  pointer-events: none;
  max-width: min(70vw, 260px);
  padding: 8px 12px;
  background: white;
  border: 1px solid rgb(228 231 238);
  border-radius: 14px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
  font-size: 0.875rem;
  font-weight: 600;
  color: rgb(26 31 46);
  word-wrap: break-word;
  white-space: pre-wrap;
  text-align: center;
  animation: mp-chat-float 6500ms ease-out forwards;
  transform-origin: center bottom;
}
.mp-chat-bubble .mp-chat-bubble-name {
  display: block;
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: rgb(136 145 168);
  margin-bottom: 2px;
}
@keyframes mp-chat-float {
  0%   { opacity: 0;    transform: translate(-50%, 10px) scale(0.85); }
  8%   { opacity: 1;    transform: translate(-50%, 0)    scale(1.0); }
  88%  { opacity: 1;    transform: translate(-50%, -10px) scale(1.0); }
  100% { opacity: 0;    transform: translate(-50%, -22px) scale(0.95); }
}

/* Chat history log — rendered inside the Chat tab's panel body.
   Empty state shows a placeholder hint.  Vanilla CSS (no `@apply`)
   so this migrates verbatim from CE's inline <style>. */
.mp-chat-log {
  max-height: 200px;
  overflow-y: auto;
  overflow-x: hidden;
  background: white;
  border: 1px solid rgb(228 231 238);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 0.875rem;
  line-height: 1.45;
  color: rgb(74 81 104);
  scrollbar-width: thin;
}
.mp-chat-log:empty::before {
  content: 'No messages yet. Type below to chat.';
  color: rgb(136 145 168);
  font-style: italic;
  font-size: 0.8125rem;
}
.mp-chat-log-row {
  padding: 3px 0;
  word-wrap: break-word;
}
.mp-chat-log-row + .mp-chat-log-row {
  border-top: 1px solid rgb(241 243 247);
}
.mp-chat-log-time {
  font-size: 0.6875rem;
  color: rgb(136 145 168);
  font-variant-numeric: tabular-nums;
  margin-right: 6px;
}
.mp-chat-log-name {
  font-weight: 700;
  color: rgb(26 31 46);
  margin-right: 6px;
}
.mp-chat-log-text { color: rgb(74 81 104); }

/* System rows (`Room` sender) — kick, leave, + Seat them, host-migration,
   and the rest of the host-initiated state-mutation events.  Visually
   distinct from typed-message rows: muted-bg pill chip for the sender
   chip, italic body text, subtle left-side accent rule.  Filed in
   `work/done/mp-kick-announces-in-chat.md` (2026-05-16). */
.mp-chat-log-row-system {
  background: rgb(248 249 252);
  border-left: 2px solid rgb(204 211 226);
  padding-left: 8px;
  margin: 2px 0;
}
.mp-chat-log-name-system {
  display: inline-block;
  background: rgb(228 231 238);
  color: rgb(74 81 104);
  font-weight: 600;
  font-size: 0.6875rem;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  border-radius: 4px;
  margin-right: 6px;
}
.mp-chat-log-text-system {
  font-style: italic;
  color: rgb(74 81 104);
}

/* ═══════════════════════════════════════════════════════════════════
   9b. mp-only-btn helper — buttons that should only render in MP
   mode (Catch! is the current consumer). Hidden by default; shown
   when body.mp-active is set.
   ═══════════════════════════════════════════════════════════════════ */

.mp-only-btn { display: none; }
body.mp-active .mp-only-btn { display: inline-flex; }

/* The "6 7" meme button uses a text glyph instead of a Unicode emoji —
   apply text typography so it reads as written numbers, not an
   oversized glyph. */
.mp-emoji-btn.mp-emoji-btn-text {
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: 0.06em;
}

/* ═══════════════════════════════════════════════════════════════════
   16. Name-prompt modal — surfaced the first time a visitor lands via
   a ?room=CODE link and needs to pick a display name before joining.
   ═══════════════════════════════════════════════════════════════════ */

.mp-name-modal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.55);
  z-index: 300;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.mp-name-modal.is-on { display: flex; }
.mp-name-card {
  background: #ffffff;
  border-radius: 14px;
  padding: 20px 22px;
  box-shadow: 0 16px 48px rgba(0,0,0,0.40);
  max-width: 420px;
  width: 100%;
}

/* ═══════════════════════════════════════════════════════════════════
   10. Emoji bar + spectator-emoji zone
   The emoji bar sits below the table during active MP play.  Floats
   animate up from a seat (player) or from `.mp-spec-zone` (spectator).
   Tailwind `@apply` directives expanded inline because this is a
   plain CSS file served outside the Tailwind Play CDN's <style
   type="text/tailwindcss"> processing.  Phase 3 of the
   `checkers-add-multiplayer.md` extract.
   ═══════════════════════════════════════════════════════════════════ */

.mp-emoji-bar {
  display: none;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: center;
  /* Symmetric 1rem vertical gap on each side (Chad, 2026-05-16 —
     tightened from the initial 2rem which read as too much air around
     a row of small buttons).
     The section above (`#mp-lobby`) uses `mb-4` (1rem), and that's
     what contributes the visible above-gap via margin-collapse — so
     the bar adds NO top margin of its own (anything ≤ 1rem gets
     dominated by the section's mb).  The board card below has no
     top margin, so the bar's own `margin-bottom: 1rem` controls the
     below-gap directly.  Net: 1rem above (from section mb), 1rem
     below (from bar mb) — symmetric and tighter. */
  margin-top: 0;
  margin-bottom: 1rem;
}
.mp-emoji-bar.is-on { display: flex; }

.mp-emoji-btn {
  /* @apply bg-white border border-rule rounded-full cursor-pointer transition-colors; */
  background: #ffffff;
  border: 1px solid #e4e7ee;
  border-radius: 9999px;
  cursor: pointer;
  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
  /* literal properties (originals): */
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0;
}
.mp-emoji-btn:hover {
  /* @apply border-accent bg-bgsoft; */
  border-color: #2f6ef5;
  background: #f8f9fc;
}
.mp-emoji-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* Floating emoji that animates up from a seat for ~2s */
.mp-emoji-float {
  position: fixed;
  z-index: 250;
  pointer-events: none;
  font-size: 2.2rem;
  line-height: 1;
  text-shadow: 0 2px 4px rgba(0,0,0,0.35);
  animation: mp-emoji-float 2000ms ease-out forwards;
}
@keyframes mp-emoji-float {
  0%   { opacity: 0; transform: translate(-50%, 0)      scale(0.6); }
  12%  { opacity: 1; transform: translate(-50%, -10px)  scale(1.0); }
  80%  { opacity: 0.95; transform: translate(-50%, -80px) scale(1.1); }
  100% { opacity: 0;    transform: translate(-50%, -120px) scale(1.0); }
}

/* ═══════════════════════════════════════════════════════════════════
   11. Spectator-specific UI — spectator zone, watching popover.
   The `.mp-spec-zone` is an invisible fixed-position anchor at the
   bottom-center of the viewport — emoji floats from spectators
   originate from this rect rather than from a seated player's
   on-table position.  Phase 3 of the `checkers-add-multiplayer.md`
   extract.
   ═══════════════════════════════════════════════════════════════════ */

.mp-spec-zone {
  position: fixed;
  bottom: 16px;
  left: 50%;
  transform: translateX(-50%);
  width: 36px;
  height: 36px;
  pointer-events: none;
  z-index: 1; /* below the floating emoji's z-index of 250 */
  opacity: 0; /* visually invisible — pure position anchor */
}

/* Watching badge popover in the in-game room strip — tapping the
   "👀 N watching" badge expands a list of spectator names. */
.mp-watch-pop {
  position: absolute;
  z-index: 60;
  background: #ffffff;
  border: 1px solid #e4e7ee;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(26, 31, 46, 0.18);
  padding: 6px 10px;
  min-width: 200px;
  font-size: 0.875rem;
  color: #1a1f2e;
  max-height: 240px;
  overflow-y: auto;
}
.mp-watch-pop .mp-watch-row {
  padding: 4px 0;
  border-bottom: 1px solid #e4e7ee;
}
.mp-watch-pop .mp-watch-row:last-child { border-bottom: none; }
.mp-watch-pop .mp-watch-empty {
  /* @apply text-muted italic; */
  color: #8891a8;
  font-style: italic;
}

/* Connection-status badge in the top-right of the table when MP is
   active.  Pip indicator showing `.is-on` (connected) / `.is-warn`
   (degraded). */
.mp-conn-badge {
  position: absolute;
  top: 6px;
  right: 8px;
  z-index: 30;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  padding: 4px 8px;
  border-radius: 999px;
  background: rgba(0,0,0,0.40);
  color: #ffffff;
  pointer-events: none;
  display: none;
}
.mp-conn-badge.is-on { display: inline-flex; align-items: center; gap: 4px; }
.mp-conn-badge.is-warn { background: rgba(245,158,11,0.95); }

/* ═══════════════════════════════════════════════════════════════════
   12. Ban list — disclosure, row, allow-back button
   Current CE source: scattered.
   ═══════════════════════════════════════════════════════════════════ */

/* (Phase 3 migrates rules here.) */

/* ═══════════════════════════════════════════════════════════════════
   13. Round-end FX — confetti pieces, skull rain, banners.
   Shared across pages; the trigger condition (which seat won, was
   it a match-clinch) comes from the consumer via the parameter
   passed to MP_SHARED.triggerRoundEndFx({localWon, isMatchOver,
   winnerName}).  The DOM elements use the `ce-fx-*` class prefix
   for historical reasons (this code originated in CE); the names
   are not CE-specific in meaning, just in lineage.

   Pure CSS, no `@apply` — moves verbatim from CE's inline <style>
   block (lines 1804-1872 pre-migration).
   ═══════════════════════════════════════════════════════════════════ */

.ce-fx-container {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9000;
  overflow: hidden;
}
.ce-fx-container.is-lose::before {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.30);
}
.ce-fx-piece {
  position: absolute;
  top: -8vh;
  width: 10px;
  height: 16px;
  border-radius: 2px;
  opacity: 0.95;
  animation: ce-fx-fall linear forwards;
  will-change: transform, top;
}
.ce-fx-piece.is-emoji {
  width: auto;
  height: auto;
  background: transparent;
  font-size: 1.6rem;
  border-radius: 0;
}
@keyframes ce-fx-fall {
  0%   { transform: translateY(0) rotate(0deg); opacity: 0; }
  8%   { opacity: 0.95; }
  100% { transform: translateY(120vh) rotate(720deg); opacity: 0.95; }
}
.ce-fx-banner {
  position: absolute;
  left: 50%;
  top: 38%;
  transform: translate(-50%, -50%);
  font-weight: 900;
  line-height: 1.0;
  letter-spacing: 0.02em;
  text-shadow: 0 4px 16px rgba(0,0,0,0.45), 0 2px 4px rgba(0,0,0,0.30);
  animation: ce-fx-banner-in 600ms cubic-bezier(0.34, 1.56, 0.64, 1.0) forwards;
  pointer-events: none;
  white-space: nowrap;
  text-align: center;
}
.ce-fx-banner.is-win {
  font-size: clamp(2rem, 9vw, 4rem);
  color: #fde047;
  text-shadow: 0 0 18px rgba(253,224,71,0.6), 0 4px 16px rgba(0,0,0,0.45);
}
.ce-fx-banner.is-lose {
  font-size: clamp(1.6rem, 7vw, 3rem);
  color: #cbd5e1;
  animation: ce-fx-banner-droop 1200ms ease-in forwards;
}
@keyframes ce-fx-banner-in {
  0%   { transform: translate(-50%, -50%) scale(0.4); opacity: 0; }
  70%  { transform: translate(-50%, -50%) scale(1.1); opacity: 1; }
  100% { transform: translate(-50%, -50%) scale(1.0); opacity: 1; }
}
@keyframes ce-fx-banner-droop {
  0%   { transform: translate(-50%, -150%) scale(0.6); opacity: 0; }
  40%  { transform: translate(-50%, -50%) scale(1.0); opacity: 1; }
  100% { transform: translate(-50%, 10%) scale(0.94); opacity: 0.6; }
}

/* ═══════════════════════════════════════════════════════════════════
   14. Mode picker (Solo / Friends)
   Per Q13: markup stays inline on each consumer page (with
   page-specific copy strings), but the visual treatment lives here
   under generic class names (`.mp-mode-card`, `.mp-mode-bar`,
   `.mp-mode-bar-switch`).  Migrated from CE's `.ce-mode-*` family
   on 2026-05-14 as the second half of the Phase 3 extract.
   ═══════════════════════════════════════════════════════════════════ */

/* Progressive-disclosure mode picker.  The two-card layout for the
   initial "How do you want to play?" decision is the first thing a
   fresh visitor sees.  Cards rather than chips because Mode is a
   qualitatively bigger decision than the secondary settings —
   Solo and Friends are fundamentally different page modes (Friends
   reveals the lobby panel, locks in peer-to-peer setup, etc.).  The
   cards visually signal "this is the primary choice" and include a
   one-line description so newcomers know which path is which. */
/* Padding is 0 — the picker is two large cards with their own internal
   padding; an outer wrapper padding was redundant once the per-game
   hero stopped pushing them down on mobile (see docs/games.md "Hero
   is hidden on phone-sized screens" — without that visual mass above
   the picker, the original 6/4 padding combined with main + controls-
   section padding into a too-airy stack). The two cards already have
   border, padding, and a clear visual edge, so they don't need an
   extra outer band. */
.mp-mode-picker { padding: 0; }
.mp-mode-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
@media (min-width: 560px) {
  .mp-mode-cards { grid-template-columns: 1fr 1fr; gap: 14px; }
}
.mp-mode-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: 4px;
  padding: 14px 16px;
  background: #ffffff;
  border: 1.5px solid #e4e7ee;
  border-radius: 12px;
  cursor: pointer;
  transition: border-color 140ms, background-color 140ms, transform 80ms;
  font: inherit;
  color: inherit;
}
.mp-mode-card:hover {
  border-color: #2f6ef5;
  background: #f5f8ff;
}
.mp-mode-card:active { transform: translateY(1px); }
.mp-mode-card:focus-visible {
  outline: none;
  border-color: #2f6ef5;
  box-shadow: 0 0 0 3px rgba(47, 110, 245, 0.18);
}
.mp-mode-card:disabled {
  opacity: 0.40;
  filter: grayscale(0.85);
  cursor: not-allowed;
}
.mp-mode-card-title {
  font-size: 1.0625rem;
  font-weight: 700;
  color: #1a1f2e;
}
.mp-mode-card-desc {
  font-size: 0.8125rem;
  color: #4a5168;
  line-height: 1.4;
}

/* Mode bar — current mode displayed prominently on the left so the
   visitor is always certain which mode they're in; the OFF-mode
   switch link sits on the right (smaller, underlined accent-blue)
   so it reads as "alternate path," not "current state."  Earlier
   iteration had only the switch link on the right — too easy to
   misread as the current mode. */
.mp-mode-bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 0 0 8px;
}
.mp-mode-bar-current {
  font-size: 1.125rem;
  font-weight: 700;
  color: #1a1f2e;
  letter-spacing: 0.005em;
}
.mp-mode-bar-switch {
  font-size: 0.8125rem;
  font-weight: 600;
  color: #2f6ef5;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 2px 0;
}
.mp-mode-bar-switch:hover { color: #1a4fc4; }
.mp-mode-bar-switch:disabled {
  opacity: 0.40;
  cursor: not-allowed;
  text-decoration: none;
}

/* Toggle visibility based on body.mode-picked.  Until a mode is
   chosen, only the picker is shown — the bar (and any consumer-
   specific settings row) is hidden.  After picking, the picker
   collapses and the bar reveals.  The per-page settings row visibility
   stays on the consumer page (CE's `.ce-settings-row`, checkers's
   equivalent) because the row's class name varies by game. */
body:not(.mode-picked) .mp-mode-bar {
  display: none;
}
body.mode-picked .mp-mode-picker {
  display: none;
}

/* ═══════════════════════════════════════════════════════════════════
   15. Mobile / responsive
   The `@media (max-width: 700px)` overrides that tighten the lobby
   panel + chat block on phones.  Currently mixed into CE's main
   <style> block; will be consolidated here during Phase 3.
   ═══════════════════════════════════════════════════════════════════ */

/* (Phase 3 migrates rules here.) */
