/* ============================================================
   Cribbage Den — First-Time Player Tutorial (web)
   Loaded by both gateway.html and the React app (single source).
   All class names are prefixed .cdt- to avoid collisions.
   ============================================================ */

.cdt-overlay {
  position: fixed;
  inset: 0;
  z-index: 100000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  background:
    radial-gradient(ellipse at center, rgba(20, 87, 43, 0.97) 0%, rgba(8, 40, 20, 0.99) 100%);
  font-family: Georgia, 'Times New Roman', serif;
  color: #f5ecd7;
  animation: cdt-fade-in 0.35s ease;
  -webkit-user-select: none;
  user-select: none;
}

@keyframes cdt-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.cdt-panel {
  position: relative;
  width: min(1160px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  border: 2px solid #d4af37;
  border-radius: 14px;
  background: linear-gradient(160deg, #0f4423 0%, #0a3018 100%);
  box-shadow:
    0 0 0 6px rgba(8, 40, 20, 0.6),
    0 18px 60px rgba(0, 0, 0, 0.55),
    inset 0 0 40px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

/* ── Header row ─────────────────────────────────────────── */
.cdt-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px 10px;
}

.cdt-heading {
  margin: 0;
  font-size: 1.25em;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: #d4af37;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}

.cdt-exit {
  appearance: none;
  border: 1px solid rgba(212, 175, 55, 0.5);
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.25);
  color: #e8d9a8;
  font-family: inherit;
  font-size: 0.8em;
  padding: 6px 14px;
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}

.cdt-exit:hover,
.cdt-exit:focus-visible {
  background: rgba(212, 175, 55, 0.2);
  color: #fff;
}

/* ── Stage (mock game table) ────────────────────────────── */
.cdt-stage {
  position: relative;
  width: min(100%, calc((100vh - 172px) * 2.16));
  margin: 0 auto;
  border-radius: 10px;
  /* Light-tan screen bezel (matches the iOS tutorial) — deliberately
     distinct from the bright-gold spotlight ring so the two never read
     as the same kind of highlight. Wraps the whole mock screen,
     header included. */
  border: 2px solid #dec79e;
  background: #14572b;
  box-shadow: inset 0 0 30px rgba(0, 0, 0, 0.35);
  overflow: hidden;
  /* Mirrors the landscape iPhone game canvas (844 × 390).  Constraining
     width by available viewport height keeps the whole mock table centered
     and visible instead of clipping it in a short landscape viewport. */
  aspect-ratio: 2.16 / 1;
  min-height: 0;
  flex: 0 1 auto;
}

/* Both scenes (mock table + mock lobby) and the spotlight ring share this
   wrapper. Its box is exactly the stage's PADDING box — inside the 2px
   bezel — which is what positionRing measures the ring against. */
.cdt-scenes {
  position: absolute;
  inset: 0;
  /* Lets the lobby scene size itself from the STAGE rather than the
     viewport — see .cdt-lobby's font-size. */
  container-type: size;
  container-name: cdt-stage;
}

.cdt-table,
.cdt-lobby {
  position: absolute;
  inset: 0;
  font-size: clamp(6px, 0.82vw, 10px); /* everything inside scales in em */
}

/* The lobby's modal is a stack of fixed-height rows, so it has to shrink
   with the stage or the join list runs off the bottom. The stage's width is
   `min(100%, (100vh - 172px) * 2.16)` — height-bound in a short viewport,
   which a vw-based size can't see. 1cqw is 1% of the stage's own width, so
   this tracks both cases; the vw rule above stays as the fallback for
   browsers without container query units. */
@supports (width: 1cqw) {
  .cdt-lobby { font-size: clamp(4.5px, 0.9cqw, 10px); }
}

/* One scene at a time: the tour opens on the lobby (home screen → Create /
   Join modals) and moves to the table for the rest. */
.cdt-lobby { display: none; }
.cdt-stage.cdt-scene-lobby .cdt-table { display: none; }
.cdt-stage.cdt-scene-lobby .cdt-lobby { display: block; }

/* Every highlightable element dims unless spotlighted */
.cdt-el {
  transition: opacity 0.35s ease, filter 0.35s ease;
}

.cdt-stage.cdt-dimmed .cdt-el {
  opacity: 0.22;
  filter: saturate(0.6);
}

.cdt-stage.cdt-dimmed .cdt-el.cdt-spot {
  opacity: 1;
  filter: none;
}

.cdt-spot-ring {
  position: absolute;
  /* border-box, or the 2px border inflates the sized box down-right and
     the ringed button sits visibly off-centre */
  box-sizing: border-box;
  border: 2px solid #ffd700;
  border-radius: 10px;
  box-shadow: 0 0 14px rgba(255, 215, 0, 0.85), inset 0 0 10px rgba(255, 215, 0, 0.35);
  pointer-events: none;
  opacity: 0;
  transition: all 0.4s ease;
  animation: cdt-ring-pulse 1.6s ease-in-out infinite;
  z-index: 40;
}

.cdt-spot-ring.cdt-on { opacity: 1; }

@keyframes cdt-ring-pulse {
  0%, 100% { box-shadow: 0 0 10px rgba(255, 215, 0, 0.6), inset 0 0 8px rgba(255, 215, 0, 0.25); }
  50%      { box-shadow: 0 0 20px rgba(255, 215, 0, 1),   inset 0 0 14px rgba(255, 215, 0, 0.45); }
}

/* ── Mock header bar ────────────────────────────────────── */
.cdt-mock-header {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 11.5%;
  display: flex;
  align-items: center;
  padding: 0 3.3%;
  box-sizing: border-box;
  background: #063719;
  border-bottom: 0.16em solid rgba(212, 175, 55, 0.48);
}

.cdt-hbtn {
  width: 2.7em; height: 2.7em;
  display: flex; align-items: center; justify-content: center;
  border-radius: 0.8em;
  font-size: 0.9em;
  color: #f5ecd7;
  margin-right: 0.75em;
}

.cdt-hbtn.cdt-menu {
  width: 3.1em;
  flex-direction: column;
  gap: 0.4em;
  margin-right: 1.15em;
}

.cdt-hbtn.cdt-menu i {
  width: 2.35em;
  height: 0.3em;
  border-radius: 99px;
  background: #f5ecd7;
}

.cdt-hbtn svg {
  width: 1.65em;
  height: 1.65em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cdt-hbtn.cdt-log { background: #a46717; }

/* Chat / voice buttons exist only in online games — shown on the online
   steps. Colours match the real header's buttons (PageHeaderModal.css:
   chat #2563EB blue, video #16A34A green, white icons). */
.cdt-hbtn.cdt-chat,
.cdt-hbtn.cdt-voice { display: none; color: #fff; }

.cdt-hbtn.cdt-chat  { background: #2563EB; }
.cdt-hbtn.cdt-voice { background: #16A34A; }

.cdt-stage.cdt-online .cdt-hbtn.cdt-chat,
.cdt-stage.cdt-online .cdt-hbtn.cdt-voice { display: flex; }

/* Live idle turn-timer readout, right of the voice button — mirrors the real
   header's .turn-timer-readout (PageHeaderModal.css). The real one is only
   rendered while a clock is armed on the local seat, so the mock shows it on
   the Turn Timer step alone. */
.cdt-turntimer {
  display: none;
  align-items: center;
  gap: 0.3em;
  margin-right: 0.75em;
  padding: 0.4em 0.7em;
  border-radius: 0.6em;
  background: rgba(0, 0, 0, 0.32);
  color: #e8eef7;
  font-family: Arial, Helvetica, sans-serif;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 1em;
  line-height: 1;
  white-space: nowrap;
}

.cdt-stage.cdt-timer .cdt-turntimer { display: inline-flex; }

.cdt-turntimer svg {
  width: 1.1em;
  height: 1.1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
}

.cdt-tt-val {
  min-width: 2.4ch;
  text-align: right;
}

/* Under six seconds the real readout turns red and pulses. */
.cdt-turntimer.cdt-tt-urgent {
  background: rgba(220, 38, 38, 0.9);
  color: #fff;
  animation: cdt-tt-pulse 1s ease-in-out infinite;
}

.cdt-turntimer.cdt-tt-urgent svg { opacity: 1; }

@keyframes cdt-tt-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.55); }
  50%      { box-shadow: 0 0 0 0.45em rgba(220, 38, 38, 0); }
}

.cdt-mock-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.5em 1.45em;
  border: 0.15em solid #d4af37;
  border-radius: 0.65em;
  background: #0c3a1d;
  color: #ffd700;
  font-size: 1.45em;
  font-weight: 700;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* Online steps swap "Local Game" for the shareable game code, like the
   real header does. */
.cdt-mock-gamecode {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: none;
  align-items: center;
  gap: 0.6em;
  font-size: 1.3em;
  white-space: nowrap;
}

.cdt-gc-label {
  color: #f5ecd7;
  font-weight: 700;
}

.cdt-gc-code {
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: #ffd700;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid rgba(212, 175, 55, 0.6);
  border-radius: 0.4em;
  padding: 0.18em 0.6em 0.18em 0.72em;
}

.cdt-stage.cdt-online .cdt-mock-title { display: none; }
.cdt-stage.cdt-online .cdt-mock-gamecode { display: flex; }

/* Room options (seat cap / idle-kick / lock), mirroring the real header's
   RoomOptionsDisplay: large emoji glyphs with the chosen value overlaid in
   black with a white outline. Anchored in the right half of the header,
   between the centered game code and the right-hand avatar. Shown on the
   online steps. */
.cdt-mock-roomopts {
  position: absolute;
  /* Anchored just left of the right-hand avatar, hugging its icons so the
     spotlight ring wraps them tightly rather than the whole right half. */
  right: 4.6em;
  top: 0; bottom: 0;
  display: none;
  align-items: center;
  gap: 1.15em;
  pointer-events: none;
}

.cdt-stage.cdt-online .cdt-mock-roomopts { display: flex; }

.cdt-ro {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.cdt-ro-emoji {
  font-size: 2em;
  line-height: 1;
  text-shadow:
    1px 1px 0 #fff, -1px 1px 0 #fff, 1px -1px 0 #fff, -1px -1px 0 #fff,
    1px 0 0 #fff, -1px 0 0 #fff, 0 1px 0 #fff, 0 -1px 0 #fff;
}

.cdt-ro-emoji--lock { font-size: 1.6em; }

.cdt-ro-val {
  position: absolute;
  left: 50%;
  top: 50%;
  /* Nudge down: the emoji glyph renders slightly above its em-box centre, so
     the value lands on the icon's visual centre (same trick as the real one). */
  transform: translate(-50%, calc(-50% + 2px));
  font-size: 1.05em;
  font-weight: 800;
  line-height: 1;
  color: #000;
  white-space: nowrap;
  font-family: Arial, Helvetica, sans-serif;
  text-shadow:
    1px 1px 0 #fff, -1px 1px 0 #fff, 1px -1px 0 #fff, -1px -1px 0 #fff,
    1px 0 0 #fff, -1px 0 0 #fff, 0 1px 0 #fff, 0 -1px 0 #fff;
}

.cdt-ro-val--idle { font-size: 0.85em; }

/* Monogram avatar like the game's UserAvatar fallback: a coloured circle
   with the player's white uppercase initial and a soft white border
   (UserAvatar.css). Blue matches the Player Ranks popup's avatar
   (.cdt-rc-avatar) so both clearly show the same player. */
.cdt-avatar {
  width: 2.8em; height: 2.8em;
  margin-left: auto;
  border-radius: 50%;
  border: 0.14em solid rgba(255, 255, 255, 0.85);
  box-sizing: border-box;
  background: #0b72c4;
  box-shadow: 0 0.07em 0.2em rgba(0, 0, 0, 0.25);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 1em;
}

.cdt-avatar span {
  font-family: Arial, Helvetica, sans-serif;
  font-weight: 600;
  font-size: 1.4em;
  line-height: 1;
  text-transform: uppercase;
}

/* ── Mock board ─────────────────────────────────────────── */
/* Horizontal, sitting in a band below the header — the real DESKTOP WEB
   layout (unlike the iOS tutorial, which rotates the board vertical into a
   left-hand column to match the iPhone's portrait-locked landscape game
   screen). The art is the REAL desktop board
   (public/Cribbage_Board_Desktop.svg, a copy of the game's own SVG with
   the placeholder count blanked), which includes the Round Total box in
   its bottom-left corner. Top 14% clears the 11.5%-tall header; the
   board's bottom (~43%) clears the pill row at 47% — keep those gaps if
   resizing. */
.cdt-mock-board {
  position: absolute;
  top: 13%;
  left: 25%;
  width: 50%;
  z-index: 3;
}

.cdt-mock-board img {
  display: block;
  width: 100%;
  height: auto;
}

/* Live count overlaid on the board art's Round Total box (inner rect
   x 33.66–229.12, number baseline y 517.66 at 72px Arial Bold in the
   SVG's 2053.6 × 553.56 viewBox → centre ≈ 6.4% / 88.9% of the board).
   Recalibrate here if the board art ever changes. */
.cdt-count {
  position: absolute;
  left: 6.4%;
  top: 88.9%;
  transform: translate(-50%, -50%);
  font-family: Arial, Helvetica, sans-serif;
  font-weight: 700;
  font-size: 2.1em;
  line-height: 1;
  color: #1a1a1a;
  z-index: 5;
}

/* Above-zero white glow on the Round Total number — the same effect the
   real board applies to its stackTotalPlaceholder whenever the count is
   over 0 (CribbageBoard.js stackTotalPulseClass). Toggled by setCount()
   in tutorial.js. */
.cdt-count.cdt-count-live {
  color: #000;
  text-shadow: 0 0 5px #fff, 0 0 8px #fff;
}

/* Pegs are positioned as % of the BOARD's own box (not the stage) — see
   LANE_X/LANE_Y in tutorial.js, calibrated against the top two lanes
   (blue = "you", red = "opp") of Cribbage Board.png. */
/* Each peg is an inline SVG of the game's default "newPeg" 3D turned peg
   (see pegSVG in tutorial.js). The SVG's hole line sits ~96% down its
   viewBox (the last 4% is socket-shadow bleed), so the base transform
   anchors that line — not the box bottom — on the hole coordinate. */
.cdt-peg {
  position: absolute;
  width: 1.5%;
  transition: left 0.45s ease, top 0.45s ease;
  transform: translate(-50%, -95.7%); /* hole line at 199/208 of the viewBox */
  z-index: 4;
}

.cdt-peg svg {
  display: block;
  width: 100%;
  height: auto;
}

.cdt-peg-defs {
  position: absolute;
  width: 0;
  height: 0;
}

/* Default/resting positions: parked in the board art's STARTING PEG AREA,
   in the REAL hole centres measured from the SVG's circles (viewBox
   2053.6 × 553.56). The start grid runs in ROWS — top row blue ("you",
   y 65.8), middle row red (opponent, y 108.8) — and, like the game at
   score 0, all THREE pegs are seated: the game-counter peg in the row's
   1st hole (x 228.1, where it stays for good) and the two scoring pegs
   in the 2nd and 3rd (x 264.0 / 299.2), lead peg nearest the track. Keep
   in sync with START_X/START_Y in tutorial.js, which uses the same holes
   as the demos' reset position. */
.cdt-peg-you.cdt-peg-game  { left: 11.11%; top: 11.89%; }
.cdt-peg-you.cdt-peg-lead  { left: 14.57%; top: 11.89%; }
.cdt-peg-you.cdt-peg-trail { left: 12.86%; top: 11.89%; }
.cdt-peg-opp.cdt-peg-game  { left: 11.11%; top: 19.66%; }
.cdt-peg-opp.cdt-peg-lead  { left: 14.57%; top: 19.66%; }
.cdt-peg-opp.cdt-peg-trail { left: 12.86%; top: 19.66%; }

.cdt-peg-hop { animation: cdt-peg-hop 0.45s ease; }

@keyframes cdt-peg-hop {
  0%, 100% { transform: translate(-50%, -95.7%) scale(1); }
  45%      { transform: translate(-50%, -95.7%) translateY(-0.9em) scale(1.15); }
}

/* ── Player columns ─────────────────────────────────────── */
/* Each player is a centered COLUMN like the real desktop game: pill on
   top, hand directly beneath it, played pile beneath that — all sharing
   the column's centre line ("you" at 36%, opponent at 72%) via
   translateX(-50%). The flight/tap keyframes below derive their start
   and landing coordinates from these centres — recalibrate them together. */
.cdt-pill {
  position: absolute;
  top: 47%;
  display: flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.35em 1em;
  border-radius: 2em;
  background: #0c2f18;
  border: 0.14em solid #0a2412;
  color: #ffd700;
  font-style: italic;
  font-weight: 700;
  font-size: 1.05em;
  letter-spacing: 0.05em;
  white-space: nowrap;
  z-index: 5;
}

.cdt-pill .cdt-dot {
  width: 0.7em; height: 0.7em;
  border-radius: 50%;
}

.cdt-pill .cdt-chip {
  background: #ffd700;
  color: #10331c;
  border-radius: 50%;
  width: 1.5em; height: 1.5em;
  display: flex; align-items: center; justify-content: center;
  font-style: normal;
  font-size: 0.85em;
}

.cdt-pill .cdt-dealer {
  background: #f5ecd7;
  color: #10331c;
  border-radius: 50%;
  width: 1.5em; height: 1.5em;
  display: flex; align-items: center; justify-content: center;
  font-style: normal;
  font-size: 0.85em;
  font-weight: 700;
}

.cdt-pill.cdt-you  { left: 36%; transform: translateX(-50%); box-shadow: 0 0 0.9em 0.15em rgba(255, 215, 0, 0.8); }
.cdt-pill.cdt-opp  { left: 72%; transform: translateX(-50%); }

/* ── Mini cards ─────────────────────────────────────────── */
/* Real card art (public/tutorial-cards, copied from the game's own PNGs —
   see CARD_IMG in tutorial.js) instead of drawn rank/suit text, matching
   the iOS tutorial's use of real CardView assets. Ratio/radius mirror the
   real game's --card-ratio (2.5/3.5) and --border-radius (3/50 of height). */
.cdt-card {
  width: 4.4em; height: 6.16em;
  border-radius: 0.34em;
  background: #fff;
  border: 1px solid #888;
  box-shadow: 0.1em 0.2em 0.4em rgba(0, 0, 0, 0.35);
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Face-up art gets the game's white outer margin (.card-realistic-image:
   97% contained, radius × 0.7); face-down backs stay full-bleed cover,
   exactly like the real game's face-down override. */
.cdt-card img {
  display: block;
  width: 97%;
  height: 97%;
  object-fit: contain;
  border-radius: 0.24em;
}

.cdt-card.cdt-back img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
}

/* ── Coverable cards (Scoring demo reveals) ─────────────── */
/* Cards in a .cdt-coverable group are REAL faces wearing a full-bleed
   card-back cover (identical to a .cdt-back card). .cdt-covered shows the
   covers; swapping it for .cdt-revealing flips the whole group over at
   once (the groups themselves chain via startScoring's beats). */
.cdt-coverable .cdt-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: url('/tutorial-cards/card_back.png') center / cover no-repeat;
  opacity: 0;
  pointer-events: none;
}

.cdt-coverable.cdt-covered .cdt-card::after { opacity: 1; }

.cdt-coverable.cdt-revealing { perspective: 24em; }

.cdt-coverable.cdt-revealing .cdt-card {
  animation: cdt-reveal-flip 0.5s ease both;
}

.cdt-coverable.cdt-revealing .cdt-card::after {
  animation: cdt-reveal-cover 0.5s steps(1, end) both;
}

@keyframes cdt-reveal-flip {
  0%   { transform: rotateY(0deg); }
  50%  { transform: rotateY(88deg); }
  100% { transform: rotateY(0deg); }
}

/* The cover disappears exactly at the flip's edge-on midpoint. */
@keyframes cdt-reveal-cover {
  0%, 49.9% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* Hands sit a beat below the pill row (pill bottom ≈ 51%) so the pill
   never crowds the cards; the fan step is a tight 1.3em like the real
   game's overlapped hand. */
.cdt-hand {
  position: absolute;
  top: 55%;
  display: flex;
  z-index: 4;
}

/* The LAST card must not carry the fan's negative margin — it would
   shrink the flex box by a card-overlap and skew translateX(-50%)
   centering half a card to the right. */
.cdt-hand .cdt-card { margin-right: -3.1em; }
.cdt-hand .cdt-card:last-of-type { margin-right: 0; }

/* Hands show the played-cards-removed counts on every step where the
   piles hold cards: 2 for you (your pile shows 2 played), 3 for the
   opponent (theirs shows 1). The kept run's last visible card takes over
   the "last card" zero-margin, since the true last-of-type is hidden and
   would otherwise leave a trailing overlap gap that skews the hand's
   translateX(-50%) centering. */
.cdt-hand.cdt-you .cdt-card:nth-child(n+3) { display: none; }
.cdt-hand.cdt-you .cdt-card:nth-child(2) { margin-right: 0; }
.cdt-hand.cdt-opp .cdt-card:nth-child(n+4) { display: none; }
.cdt-hand.cdt-opp .cdt-card:nth-child(3) { margin-right: 0; }

/* Feeding the Crib: the full pre-discard six in both hands, and the
   piles sit empty — nothing has been played yet. */
.cdt-anim-discard .cdt-hand.cdt-you .cdt-card:nth-child(n+3),
.cdt-anim-discard .cdt-hand.cdt-opp .cdt-card:nth-child(n+4) { display: flex; }
.cdt-anim-discard .cdt-hand.cdt-you .cdt-card:nth-child(2),
.cdt-anim-discard .cdt-hand.cdt-opp .cdt-card:nth-child(3) { margin-right: -3.1em; }
.cdt-anim-discard .cdt-pile .cdt-card { display: none; }

/* Scoring: the tally gathers the played piles back into the hands, so
   the full 4-card hands flip over and the piles sit empty. */
.cdt-anim-scoring .cdt-hand.cdt-you .cdt-card:nth-child(-n+4),
.cdt-anim-scoring .cdt-hand.cdt-opp .cdt-card:nth-child(-n+4) { display: flex; }
.cdt-anim-scoring .cdt-hand.cdt-you .cdt-card:nth-child(2),
.cdt-anim-scoring .cdt-hand.cdt-opp .cdt-card:nth-child(3) { margin-right: -3.1em; }
.cdt-anim-scoring .cdt-hand .cdt-card:nth-child(4) { margin-right: 0; }
.cdt-anim-scoring .cdt-pile .cdt-card { display: none; }

.cdt-hand.cdt-you { left: 36%; transform: translateX(-50%); }
.cdt-hand.cdt-opp { left: 72%; transform: translateX(-50%); }

/* ── Play piles ─────────────────────────────────────────── */
.cdt-pile {
  position: absolute;
  top: 75%;
  display: flex;
  z-index: 3;
}

.cdt-pile .cdt-card { margin-right: -2.9em; }
.cdt-pile .cdt-card:last-of-type { margin-right: 0; }

.cdt-pile.cdt-you { left: 36%; transform: translateX(-50%); }
.cdt-pile.cdt-opp { left: 72%; transform: translateX(-50%); }

/* Flying card demo (play + discard) */
.cdt-fly {
  position: absolute;
  z-index: 30;
  opacity: 0;
  perspective: 24em;
}

.cdt-anim-play .cdt-fly.cdt-fly-play {
  animation: cdt-fly-play 2.4s ease-in-out infinite;
}

/* Endpoint calibration (lesson learned on iOS): keyframe the card's
   REAL start and landing spots. The hand is CENTERED at 36% (width
   10.9em → left edge 36% − 5.45em); the J is its 6th card (+5 fan steps
   of 1.3em → 36% + 1.05em). It lands on the centered pile's next slot
   (pile edge 36% − 2.95em + 2 steps of 1.5em → 36% + 0.05em, top 75%). */
@keyframes cdt-fly-play {
  0%       { opacity: 0; top: 55%; left: calc(36% + 1.05em); transform: scale(1); }
  12%      { opacity: 1; }
  55%      { opacity: 1; top: 75%; left: calc(36% + 0.05em); transform: scale(1); }
  70%, 100%{ opacity: 0; top: 75%; left: calc(36% + 0.05em); }
}

/* The landing target lights up in the game's blue drop-zone colour just
   as the flying card arrives (55% of the flight cycle) — mirrors the
   real drop-target highlight instead of the card landing on a dead pile. */
.cdt-anim-play .cdt-pile.cdt-you {
  border-radius: 0.6em;
  animation: cdt-pile-catch 2.4s ease-in-out infinite;
}

@keyframes cdt-pile-catch {
  0%, 40%   { box-shadow: 0 0 0 0 rgba(51, 168, 255, 0); }
  55%, 68%  { box-shadow: 0 0 1.4em 0.35em rgba(51, 168, 255, 0.8); }
  82%, 100% { box-shadow: 0 0 0 0 rgba(51, 168, 255, 0); }
}

.cdt-anim-discard .cdt-fly.cdt-fly-crib1 { animation: cdt-fly-crib 2.8s ease-in-out infinite; }
.cdt-anim-discard .cdt-fly.cdt-fly-crib2 { animation: cdt-fly-crib 2.8s ease-in-out 0.35s infinite; }

/* Same endpoint calibration: lift from the hand's 5th card (36% − 5.45em
   + 4 steps of 1.3em → 36% − 0.25em), land inside the crib box (crib
   content sits at left 2% + 0.9em pad, top 51% + 1em pad). */
@keyframes cdt-fly-crib {
  0%       { opacity: 0; top: 55%; left: calc(36% - 0.25em); transform: rotate(0deg); }
  12%      { opacity: 1; }
  60%      { opacity: 1; top: calc(51% + 1em); left: calc(2% + 0.9em); transform: rotate(-2deg); }
  75%, 100%{ opacity: 0; top: calc(51% + 1em); left: calc(2% + 0.9em); }
}

/* Discard cards flip face-up → face-down mid-flight, turning over
   top-over-bottom (rotateX, matching the real discard flight and the
   iOS tutorial) — a face and a pre-flipped back share the wrapper, each
   hiding its backface. */
.cdt-flip {
  position: relative;
  width: 4.4em;
  height: 6.16em;
  transform-style: preserve-3d;
}

.cdt-flip .cdt-card {
  position: absolute;
  inset: 0;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

.cdt-flip .cdt-card.cdt-flip-back { transform: rotateX(180deg); }

.cdt-anim-discard .cdt-fly-crib1 .cdt-flip { animation: cdt-crib-flip 2.8s ease-in-out infinite; }
.cdt-anim-discard .cdt-fly-crib2 .cdt-flip { animation: cdt-crib-flip 2.8s ease-in-out 0.35s infinite; }

@keyframes cdt-crib-flip {
  0%, 12%   { transform: rotateX(0deg); }
  55%, 100% { transform: rotateX(-180deg); }
}

/* ── Player Ranks demo: tapping hand + player-card popup ── */
/* Fingertip calibration (lesson learned on iOS): the keyframed left/top
   coordinate must be where the VISIBLE fingertip lands, not the SVG's
   frame corner. The negative margins shift the element so the glyph's
   tip (≈46% across, ≈8% down the viewBox) sits exactly on the
   keyframed point — retune the margins, not the keyframes, if the SVG
   art changes. */
.cdt-fly-hand {
  width: 3.6em;
  height: 3.6em;
  margin-left: -1.66em;
  margin-top: -0.29em;
  z-index: 33;
}

.cdt-fly-hand svg {
  width: 100%;
  height: 100%;
  fill: #fff;
  stroke: rgba(0, 0, 0, 0.55);
  stroke-width: 0.5;
  filter: drop-shadow(0 0.15em 0.3em rgba(0, 0, 0, 0.55));
}

/* Tip target = centre of the "YOUR TURN" pill (pill CENTERED at 36%,
   top 47%, ≈2em tall at its 1.05em font → centre ≈ 36% / 47% + 1em). */
.cdt-anim-ranks .cdt-fly-hand {
  animation: cdt-hand-tap 2.6s ease-in-out infinite;
}

@keyframes cdt-hand-tap {
  0%        { opacity: 0; left: 50%; top: 70%; transform: scale(1); }
  14%       { opacity: 1; }
  38%       { left: 36%; top: calc(47% + 1em); transform: scale(1); }
  46%       { transform: scale(0.85); } /* press */
  54%       { left: 36%; top: calc(47% + 1em); transform: scale(1); }
  80%       { opacity: 1; left: 36%; top: calc(47% + 1em); }
  100%      { opacity: 0; left: 36%; top: calc(47% + 1em); }
}

/* Mock of the real player-card popup: avatar, name, Rank + Wins. */
.cdt-rankcard {
  position: absolute;
  left: 30%;
  top: 54%;
  display: flex;
  align-items: center;
  gap: 0.9em;
  padding: 0.9em 1.3em;
  background: #fff;
  color: #111;
  border: 1px solid #ccc;
  border-radius: 0.9em;
  box-shadow: 0 0.5em 1.6em rgba(0, 0, 0, 0.35);
  font-family: Arial, Helvetica, sans-serif;
  opacity: 0;
  z-index: 32;
}

.cdt-rc-avatar {
  width: 3em;
  height: 3em;
  border-radius: 50%;
  background: #0b72c4;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1.15em;
}

.cdt-rc-info b { font-size: 1.1em; }

.cdt-rc-stats {
  display: flex;
  gap: 1.4em;
  margin-top: 0.35em;
}

.cdt-rc-stats span {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.2;
}

.cdt-rc-stats i {
  font-style: normal;
  font-size: 0.72em;
  color: #6b6b6b;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}

.cdt-anim-ranks .cdt-rankcard {
  animation: cdt-rank-pop 2.6s ease infinite;
}

@keyframes cdt-rank-pop {
  0%, 44%   { opacity: 0; transform: translateY(0.5em); }
  54%, 84%  { opacity: 1; transform: translateY(0); }
  95%, 100% { opacity: 0; transform: translateY(0); }
}

/* ── Your Avatar demo: tapping hand + stat popup under the avatar ── */
/* Same tap-then-pop pattern as the Player Ranks demo, retargeted at the
   header avatar. Avatar centre: the header's 3.3% side padding puts its
   right edge at 96.7% of the stage; it's 2.8em wide (centre 96.7% −
   1.4em) in the 11.5%-tall header (centre ≈ 5.75%). The popup opens
   right-aligned beneath it. */
.cdt-anim-avatar .cdt-fly-hand {
  animation: cdt-hand-tap-avatar 2.6s ease-in-out infinite;
}

@keyframes cdt-hand-tap-avatar {
  0%        { opacity: 0; left: 80%; top: 40%; transform: scale(1); }
  14%       { opacity: 1; }
  38%       { left: calc(96.7% - 1.4em); top: 5.75%; transform: scale(1); }
  46%       { transform: scale(0.85); } /* press */
  54%       { left: calc(96.7% - 1.4em); top: 5.75%; transform: scale(1); }
  80%       { opacity: 1; left: calc(96.7% - 1.4em); top: 5.75%; }
  100%      { opacity: 0; left: calc(96.7% - 1.4em); top: 5.75%; }
}

.cdt-anim-avatar .cdt-rankcard {
  left: auto;
  right: 1.4%;
  top: 14%;
  animation: cdt-rank-pop 2.6s ease infinite;
}

/* "+2" scoring float — originates from the CENTRE of your played-pile
   cards (pile centered at 36%, top 75%, + half a 6.16em card ≈ 5.7% of
   stage height) and floats upward, like the real pegging float. Styled
   like the real game's .scoring-points for player 1: the player's blue
   (#33A8FF), bold, with the white glow + white border + black
   depth-shadow stack (ScoringAnimation.css), scaled to the mock's em
   size. */
.cdt-float {
  position: absolute;
  top: 80.7%;
  left: 36%;
  transform: translate(-50%, -50%);
  color: #33A8FF;
  font-size: 2.6em;
  font-weight: 700;
  text-shadow:
    0 0 0.06em #ffffff,
    0 0 0.12em #ffffff,
    0.03em 0.03em 0 #ffffff,
    -0.03em -0.03em 0 #ffffff,
    0.03em -0.03em 0 #ffffff,
    -0.03em 0.03em 0 #ffffff,
    0.05em 0.05em 0.12em rgba(0, 0, 0, 0.9),
    -0.05em 0.05em 0.12em rgba(0, 0, 0, 0.9);
  opacity: 0;
  z-index: 31;
}

.cdt-anim-pegging .cdt-float { animation: cdt-float-up 2.4s ease-out infinite; }

@keyframes cdt-float-up {
  0%, 20%  { opacity: 0; transform: translate(-50%, -50%) translateY(0); }
  35%      { opacity: 1; }
  75%      { opacity: 1; transform: translate(-50%, -50%) translateY(-2.2em); }
  100%     { opacity: 0; transform: translate(-50%, -50%) translateY(-3em); }
}

/* Hand/crib scoring float for the Scoring demo — same look as the
   pegging float; text, colour and position (centre of the scoring cards)
   are set per reveal by startScoring(), and .cdt-score-pop replays the
   rise once per beat. */
.cdt-score-float {
  position: absolute;
  transform: translate(-50%, -50%);
  font-size: 2.6em;
  font-weight: 700;
  text-shadow:
    0 0 0.06em #ffffff,
    0 0 0.12em #ffffff,
    0.03em 0.03em 0 #ffffff,
    -0.03em -0.03em 0 #ffffff,
    0.03em -0.03em 0 #ffffff,
    -0.03em 0.03em 0 #ffffff,
    0.05em 0.05em 0.12em rgba(0, 0, 0, 0.9),
    -0.05em 0.05em 0.12em rgba(0, 0, 0, 0.9);
  opacity: 0;
  z-index: 31;
  pointer-events: none;
}

.cdt-score-float.cdt-score-pop {
  animation: cdt-score-pop-up 1.5s ease-out forwards;
}

@keyframes cdt-score-pop-up {
  0%   { opacity: 0; transform: translate(-50%, -50%) translateY(0) scale(0.85); }
  15%  { opacity: 1; transform: translate(-50%, -50%) translateY(-0.15em) scale(1); }
  70%  { opacity: 1; transform: translate(-50%, -50%) translateY(-1.7em); }
  100% { opacity: 0; transform: translate(-50%, -50%) translateY(-2.4em); }
}

/* ── Deck / cut / crib ──────────────────────────────────── */
/* Both areas wear the real table's zone-frame chrome: a thin gold box
   with the caption straddling the top border (the caption's solid
   stage-green background punches the gap in the border line, so the
   stroke never runs through the title — same fix as the iOS zone
   boxes). */
.cdt-deck,
.cdt-crib {
  border: 0.16em solid rgba(212, 175, 55, 0.85);
  border-radius: 0.8em;
}

/* Crib cards sit directly above the deck, both in a left-hand column —
   the real desktop layout: the crib is an overlapped fan of four
   face-down cards top-aligned with the hand row (51% ≈ hands' 53%), and
   the deck below aligns with the pile row. */
.cdt-crib {
  position: absolute;
  top: 51%;
  left: 2%;
  display: flex;
  z-index: 4;
  padding: 1em 0.9em 0.7em;
}

.cdt-crib .cdt-card { margin-right: -2.6em; }
.cdt-crib .cdt-card:last-of-type { margin-right: 0; }

/* Every step shows the crib at its true scored size (4 cards) except
   Feeding the Crib, which shows the lighter two-card stack mid-discard,
   before the crib is complete. */
.cdt-anim-discard .cdt-crib .cdt-card:nth-child(n+3) { display: none; }
.cdt-anim-discard .cdt-crib .cdt-card:nth-child(2) { margin-right: 0; }

/* Deck stack matching the real game's CardStack: the face-down deck
   cascades slightly down-right (the game's per-card offsets are
   card-height/800 down and card-width/194 right, accumulated over the
   deck), and the face-up starter sits FLUSH on top as the last card of
   the stack — never raised above it. All cards share one wrapper origin
   so they stay pixel-aligned. The box is centered under the crib zone
   above it: crib content is 11.6em wide vs the deck's 6.8em → offset by
   half the difference. */
.cdt-deck {
  position: absolute;
  top: 73%;
  left: calc(2% + 2.4em);
  z-index: 4;
  padding: 1em 1.5em 0.9em 0.9em;
}

.cdt-deck-stack {
  position: relative;
}

.cdt-deck-stack .cdt-card:nth-of-type(2) {
  position: absolute;
  top: 0.1em;
  left: 0.3em;
}

.cdt-deck .cdt-card.cdt-cut {
  position: absolute;
  top: 0.2em;
  left: 0.6em;
}

.cdt-tag {
  position: absolute;
  top: -0.75em;
  left: 50%;
  transform: translateX(-50%);
  padding: 0 0.55em;
  background: #14572b; /* stage felt — hides the border under the title */
  font-size: 0.8em;
  font-weight: 700;
  color: #d4af37;
  letter-spacing: 0.1em;
  white-space: nowrap;
}

/* ── Action button ──────────────────────────────────────── */
.cdt-action {
  position: absolute;
  bottom: 4%;
  left: 54%; /* midway between the two player columns (36% / 72%) */
  transform: translateX(-50%);
  min-width: 11em;
  text-align: center;
  padding: 0.7em 1.4em;
  border-radius: 0.6em;
  background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
  color: #10331c;
  font-weight: 700;
  font-size: 1.2em;
  letter-spacing: 0.04em;
  box-shadow: 0 0.25em 0.8em rgba(0, 0, 0, 0.45);
  z-index: 6;
  transition: opacity 0.25s ease;
}

.cdt-action.cdt-swap { opacity: 0; }

/* ══ Mock lobby scene ════════════════════════════════════════
   The home screen and the two modals it opens, mirroring the real gateway:
   parchment panel body (--auth-panel-bg #e7dcc3), gold frame, and a
   felt-green→wood-brown header band — see #create-game-modal /
   #game-list-modal in public/gateway-styles.css. Keep the two in step.     */

.cdt-lb-bar {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 11.5%;
  display: flex;
  align-items: center;
  gap: 1.6em;
  padding: 0 2.4%;
  box-sizing: border-box;
  background: #063719;
  border-bottom: 0.16em solid rgba(212, 175, 55, 0.48);
}

.cdt-lb-brand {
  font-size: 1.5em;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #ffd700;
  white-space: nowrap;
}

.cdt-lb-nav {
  font-size: 1.1em;
  color: rgba(245, 236, 215, 0.75);
  white-space: nowrap;
}

/* The notifications envelope and the avatar are one right-pinned pair, with a
   tighter gap than the bar's own — the bar's `gap` would otherwise apply
   between them too and set them a nav item's width apart. The wrapper carries
   the margin-left:auto .cdt-avatar has (which still pins the avatar inside
   it), so the envelope sits beside it exactly as the real nav draws them
   (.nav-invite-btn: a 34px translucent circle holding a 26px envelope in the
   accent colour, with the count on its shoulder). */
.cdt-lb-navend {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.7em;
}

.cdt-lb-bell {
  position: relative;
  flex: none;
  width: 2.8em; height: 2.8em;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  color: #ffd700;
  display: flex; align-items: center; justify-content: center;
}

.cdt-lb-bell svg {
  display: block;
  width: 2.14em; /* 26 of the button's 34, like .nav-invite-glyph */
  height: auto;
}

.cdt-lb-bell-badge {
  position: absolute;
  top: -0.2em; right: -0.2em;
  min-width: 1.35em;
  padding: 0 0.3em;
  border-radius: 0.7em;
  background: #e53935;
  color: #fff;
  font-size: 0.9em;
  font-weight: 700;
  line-height: 1.35em;
  text-align: center;
}

.cdt-lb-hero {
  position: absolute;
  top: 11.5%; left: 0; right: 0; bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.9em;
  background: radial-gradient(ellipse at center, #14572b 0%, #0a3018 100%);
}

.cdt-lb-title {
  font-size: 3.2em;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: #ffd700;
  text-shadow: 0 0.06em 0.18em rgba(0, 0, 0, 0.6);
}

.cdt-lb-sub {
  font-size: 1.3em;
  color: rgba(245, 236, 215, 0.8);
}

.cdt-lb-actions {
  display: flex;
  gap: 2.2em;
  margin-top: 1.2em;
}

.cdt-lb-btn {
  padding: 0.75em 2.4em;
  border-radius: 0.6em;
  font-size: 1.5em;
  font-weight: 700;
  letter-spacing: 0.03em;
  white-space: nowrap;
  box-shadow: 0 0.22em 0.6em rgba(0, 0, 0, 0.45);
}

.cdt-lb-create {
  background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
  color: #10331c;
}

.cdt-lb-join {
  background: rgba(0, 0, 0, 0.3);
  border: 0.11em solid #d4af37;
  color: #f5ecd7;
}

/* ── The modal ─────────────────────────────────────────── */
.cdt-lb-modal {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
}

.cdt-lobby:not(.cdt-view-home) .cdt-lb-modal { display: flex; }

/* The real Join a Game modal is the wider of the two (600px vs 500px). */
.cdt-lobby.cdt-view-join .cdt-lb-panel { width: 47em; }

/* The panel is sized in EM, not in stage percent, so it hugs its own content
   the way the real modal hugs its own — a percentage stretched it to roughly
   2.5x the real thing's width-to-type ratio, which is what made the mock read
   as a wide banner rather than a dialog. The real modals cap at 500px
   (#create-game-modal .modal-content) and 600px (.game-list-modal) over a 16px
   body font = 31.25em / 37.5em; this mock's type runs ~1.25x larger relative to
   its base for legibility at stage size, so the widths are scaled by the same
   factor. Keep these in step with iOS's lobbyPanelWidth (1lu = 0.1em there). */
.cdt-lb-panel {
  width: 39em;
  max-width: 88%;
  max-height: 88%;
  display: flex;
  flex-direction: column;
  border: 0.2em solid #ffd700;
  border-radius: 0.8em;
  background: #e7dcc3; /* --auth-panel-bg: warm aged parchment */
  color: #2c2c2c;
  box-shadow: 0 1.8em 4.8em rgba(0, 0, 0, 0.5);
  overflow: hidden;
}

/* Felt-green → wood-brown title band (--primary-color → --secondary-color). */
.cdt-lb-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85em 1.4em;
  background: linear-gradient(135deg, #2c5234 0%, #8b4513 100%);
  border-bottom: 0.2em solid #ffd700;
}

.cdt-lb-head-title {
  font-size: 1.7em;
  font-weight: 700;
  color: #fff;
  text-shadow: 0 0.06em 0.12em rgba(0, 0, 0, 0.35);
}

.cdt-lb-x {
  font-size: 1.8em;
  line-height: 1;
  color: rgba(255, 255, 255, 0.85);
}

.cdt-lb-body {
  padding: 1.4em;
  overflow: hidden;
}

.cdt-mv { display: none; }
.cdt-lobby.cdt-view-type .cdt-mv-type,
.cdt-lobby.cdt-view-room .cdt-mv-room,
.cdt-lobby.cdt-view-join .cdt-mv-join { display: block; }

/* ── View 1: choose a game type ────────────────────────── */
.cdt-gt-heading {
  text-align: center;
  color: #2c5234;
  font-size: 1.4em;
  font-weight: 700;
  margin-bottom: 1em;
}

.cdt-gt-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.4em;
}

.cdt-gt-card {
  background: #f3efe3;
  border: 0.14em solid rgba(44, 82, 52, 0.2);
  border-radius: 0.9em;
  padding: 1.4em 1.1em;
  text-align: center;
  box-shadow: 0 0.18em 0.4em rgba(0, 0, 0, 0.08);
}

.cdt-gt-icon { font-size: 3em; line-height: 1.15; }

.cdt-gt-name {
  font-size: 1.35em;
  font-weight: 700;
  color: #2c5234;
  margin: 0.35em 0 0.3em;
}

.cdt-gt-desc {
  font-size: 1.05em;
  line-height: 1.35;
  color: #5a5a5a;
}

/* ── View 2: Game Room options ─────────────────────────── */
.cdt-rop {
  display: flex;
  flex-direction: column;
  gap: 0.85em;
}

.cdt-rop-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1em;
  padding: 0.75em 1em;
  background: #f3efe3;
  border: 0.1em solid rgba(44, 82, 52, 0.15);
  border-radius: 0.6em;
}

.cdt-rop-row--stacked {
  flex-direction: column;
  align-items: stretch;
  gap: 0.45em;
}

.cdt-rop-label {
  display: flex;
  align-items: center;
  gap: 0.5em;
  font-size: 1.15em;
  font-weight: 700;
  color: #2c5234;
  white-space: nowrap;
}

.cdt-rop-emoji { font-size: 1.5em; line-height: 1; }

.cdt-rop-select {
  font-size: 1.1em;
  color: #2c2c2c;
  background: #fff;
  border: 0.1em solid #bbb;
  border-radius: 0.35em;
  padding: 0.35em 0.7em;
  white-space: nowrap;
}

.cdt-rop-input {
  font-size: 1.1em;
  color: #8b8b8b;
  background: #fff;
  border: 0.1em solid #bbb;
  border-radius: 0.35em;
  padding: 0.45em 0.7em;
}

.cdt-rop-hint { font-size: 0.95em; color: #6b6b6b; }

.cdt-rop-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.9em;
  margin-top: 1.2em;
}

.cdt-rop-btn {
  font-size: 1.15em;
  font-weight: 700;
  border-radius: 0.4em;
  padding: 0.5em 1.3em;
  white-space: nowrap;
}

.cdt-rop-cancel { background: #6c757d; color: #fff; }
.cdt-rop-create { background: #2c5234; color: #fff; }

/* ── View 3: Join a Game ───────────────────────────────── */
.cdt-jn-code {
  background: #f3efe3;
  border: 0.1em solid rgba(44, 82, 52, 0.15);
  border-radius: 0.6em;
  padding: 0.9em 1em;
  margin-bottom: 1em;
}

.cdt-jn-code-head {
  font-size: 1.2em;
  font-weight: 700;
  color: #333;
  margin-bottom: 0.6em;
}

.cdt-jn-code-form { display: flex; gap: 0.7em; }

.cdt-jn-code-input {
  flex: 1;
  font-size: 1.15em;
  letter-spacing: 0.12em;
  color: #2c2c2c;
  background: #fff;
  border: 0.1em solid #ddd;
  border-radius: 0.35em;
  padding: 0.4em 0.7em;
}

.cdt-jn-code-btn {
  font-size: 1.1em;
  font-weight: 700;
  color: #fff;
  background: #2c5234;
  border-radius: 0.35em;
  padding: 0.4em 1.1em;
}

.cdt-jn-secthead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.55em;
}

.cdt-jn-secttitle { font-size: 1.2em; font-weight: 700; color: #333; }

.cdt-jn-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font-size: 1.05em;
  font-weight: 700;
  color: #2c5234;
  background: #fff;
  border: 0.1em solid #2c5234;
  border-radius: 0.45em;
  padding: 0.35em 0.85em;
}

.cdt-jn-list {
  background: #fff;
  border: 0.1em solid rgba(44, 82, 52, 0.15);
  border-radius: 0.5em;
  overflow: hidden;
}

/* Same five columns as the real list (updateGameListDisplay /
   .game-list-header): Code, Players, Count, Options, Action. */
.cdt-jn-hrow,
.cdt-jn-row {
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 2fr) 0.8fr 1.1fr 1.5fr;
  align-items: center;
  justify-items: center;
  text-align: center;
  gap: 0.8em;
  padding: 0.5em 0.9em;
}

.cdt-jn-hrow {
  background: #f3efe3;
  border-bottom: 0.1em solid #eee;
  font-size: 1.05em;
  font-weight: 700;
  color: #333;
}

.cdt-jn-row {
  border-bottom: 0.1em solid #f0f0f0;
  font-size: 1.05em;
  color: #2c2c2c;
}

.cdt-jn-row:last-child { border-bottom: none; }

/* A room the player can rejoin is flagged, exactly like .can-rejoin. */
.cdt-jn-row--rejoin {
  background: rgba(255, 140, 0, 0.1);
  box-shadow: inset 0.3em 0 0 #ff8c00;
}

.cdt-jn-c { min-width: 0; }

.cdt-jn-c-code {
  font-family: 'Courier New', Courier, monospace;
  font-weight: 700;
  letter-spacing: 0.08em;
}

.cdt-jn-lock { margin-left: 0.3em; font-size: 0.9em; }

.cdt-jn-c-players {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}

.cdt-jn-c-opts { display: flex; align-items: center; gap: 0.6em; }

/* The Options badges reuse the .cdt-ro-* rules the table header defines,
   but on the parchment list the value needs a smaller glyph than the
   in-game header's. */
.cdt-jn-c-opts .cdt-ro-emoji { font-size: 1.7em; }
.cdt-jn-c-opts .cdt-ro-val { font-size: 0.95em; }
.cdt-jn-c-opts .cdt-ro-val--idle { font-size: 0.78em; }

.cdt-jn-btn {
  font-size: 1em;
  font-weight: 700;
  border-radius: 0.35em;
  border: 0.09em solid #000;
  padding: 0.35em 0.8em;
  white-space: nowrap;
  color: #fff;
}

.cdt-jn-btn--join { background: #28a745; }
.cdt-jn-btn--off { background: #6c757d; opacity: 0.7; }
.cdt-jn-btn--rejoin { background: #ff8c00; }

/* ── Caption / text area ────────────────────────────────── */
.cdt-text {
  padding: 14px 22px 4px;
  text-align: center;
  min-height: 92px;
}

.cdt-step-title {
  margin: 0 0 6px;
  font-size: 1.15em;
  color: #ffd700;
  letter-spacing: 0.05em;
}

.cdt-step-body {
  margin: 0 auto;
  max-width: 640px;
  font-size: 0.98em;
  line-height: 1.45;
  color: #f5ecd7;
}

/* ── Controls ───────────────────────────────────────────── */
.cdt-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 18px 16px;
}

.cdt-dots {
  display: flex;
  gap: 7px;
}

.cdt-dot-step {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(245, 236, 215, 0.25);
  transition: background 0.25s, transform 0.25s;
}

.cdt-dot-step.cdt-active {
  background: #ffd700;
  transform: scale(1.25);
}

.cdt-btn {
  appearance: none;
  font-family: inherit;
  font-size: 0.95em;
  border-radius: 8px;
  padding: 9px 22px;
  cursor: pointer;
  letter-spacing: 0.04em;
  transition: transform 0.15s, box-shadow 0.15s, background 0.2s;
}

.cdt-btn:focus-visible { outline: 2px solid #ffd700; outline-offset: 2px; }

.cdt-btn-next {
  border: none;
  background: linear-gradient(180deg, #ffd700 0%, #d4af37 100%);
  color: #10331c;
  font-weight: 700;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.4);
}

.cdt-btn-next:hover { transform: translateY(-1px); box-shadow: 0 5px 14px rgba(0, 0, 0, 0.5); }

.cdt-btn-back {
  border: 1px solid rgba(212, 175, 55, 0.5);
  background: transparent;
  color: #e8d9a8;
}

.cdt-btn-back:hover { background: rgba(212, 175, 55, 0.15); }

.cdt-btn-back[disabled] {
  opacity: 0.35;
  cursor: default;
}

.cdt-btn-back[disabled]:hover { background: transparent; }

/* ── Welcome / finish flourishes ────────────────────────── */
.cdt-stage.cdt-cover::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 30%, rgba(8, 40, 20, 0.55) 100%);
  pointer-events: none;
}

/* Small screens: let the panel scroll rather than clip */
@media (max-height: 640px) {
  .cdt-panel { overflow-y: auto; }
  .cdt-stage { min-height: 180px; }
  .cdt-text { min-height: 0; }
}
