/* === LAYOUT — play surface geometry, top bar, seat positions, breakpoints === */

/* Top bar */
.top-bar {
  position: absolute;
  top: 0; left: 0; right: 0;
  /* vh intentional — top bar height should track viewport height, not width */
  height: clamp(48px, 4.5vh, 64px);
  background: var(--paper);
  border-bottom: 1px solid rgba(0,0,0,0.06);
  display: flex;
  align-items: center;
  padding: 0 clamp(12px, 1.4vw, 24px);
  gap: 14px;
  z-index: 10;
  contain: layout style;
}

.top-bar__brand {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.top-bar__brand-icon {
  width: clamp(22px, 1.9vw, 44px);
  height: clamp(22px, 1.9vw, 44px);
  border-radius: 6px;
  background: var(--spade);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: clamp(13px, 1.1vw, 24px);
  line-height: 1;
}

.top-bar__brand-name {
  font-size: clamp(13px, 1.1vw, 24px);
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.2px;
}

.top-bar__divider {
  width: 1px; height: 16px;
  background: rgba(0,0,0,0.1);
  flex-shrink: 0;
}

.top-bar__spacer { flex: 1; }

.top-bar__paused-badge {
  background: var(--accent);
  color: #000;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.1em;
  padding: 2px 7px;
  border-radius: 999px;
  flex-shrink: 0;
}

/* Desktop section (brand + round + tally) — hidden on mobile/tablet */
.top-bar__desktop {
  display: none;
  align-items: center;
  gap: 14px;
  flex: 1;
  min-width: 0;
}

/* Mobile/tablet section — two rows.
   Row 1: table-tally + paused badge (menu button overlays top-right).
   Row 2: score + last-trick pill, justified across the row.
   min-width:0 lets flex children shrink. We deliberately do NOT set
   overflow:hidden here — that was clipping content invisible on narrow
   viewports. */
.top-bar__mobile {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 0;
}

.top-bar__mobile-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* Both top and bottom rows are position:relative so the viewport-
   centered children anchor against the full row width (= viewport
   width on mobile). Table id lives in flow at the left of the top
   row; the menu button overlays right via its own absolute rule. */
.top-bar__mobile-row--top,
.top-bar__mobile-row--bottom {
  position: relative;
  min-height: 32px;
}

/* Wrapper that absolutely-centers its child within the top row. The
   row spans the full top-bar width, so 50% + translateX(-50%) lands
   at the actual viewport center, not the visual center between left
   and right elements (which would drift with content widths). */
.top-bar__viewport-center {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  pointer-events: none;
}
.top-bar__viewport-center > * { pointer-events: auto; }

/* Table-id chip in the upper-left corner. Monospace + muted so it
   reads as a stable identifier rather than competing with score. */
.top-bar__table-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: color-mix(in oklab, var(--ink) 55%, transparent);
  padding: 2px 6px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--ink) 6%, transparent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}


@media (min-width: 900px) {
  .top-bar__desktop { display: flex; }
  .top-bar__mobile  { display: none; }
}

.top-bar__menu-btn {
  padding: 4px 8px;
  font-size: 18px;
  border-radius: 6px;
  color: var(--ink-soft);
  line-height: 1;
  flex-shrink: 0;
  margin-left: auto;
}
.top-bar__menu-btn:hover { background: rgba(0,0,0,0.05); }

/* On mobile/tablet the top-bar grows to two rows. The menu button
   sits absolute top-right so neither row has to reserve a slot for
   it (other than the small padding-right on row 1 to keep tap
   targets clear).

   Two rows need ~78-90px of vertical space (8px top pad + ~32px row 1
   + 4px gap + ~26px row 2 + 8px bottom pad). The default top-bar
   height of clamp(48px, 4.5vh, 64px) is too short — leaving row 2
   to overflow into the play surface and crowd the north seat-chip.
   Drop the height clamp and bump the play-surface offset on both
   tablet and phone. */
@media (max-width: 899px) {
  .top-bar {
    height: auto;
    padding: 8px 12px;
  }
  .top-bar__menu-btn {
    position: absolute;
    top: 8px;
    right: 12px;
    margin-left: 0;
  }
  .play-surface {
    top: 88px;
  }
}

/* Build-version badge in the top bar — always visible so a stale-cache
   session can be diagnosed at a glance. Subtle, monospace, muted color
   so it doesn't compete with the score / tally. */
.top-bar__build {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: color-mix(in oklab, var(--ink) 45%, transparent);
  padding: 2px 5px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--ink) 6%, transparent);
  flex-shrink: 0;
  user-select: text;
  -webkit-user-select: text;
}
@media (max-width: 559px) {
  .top-bar__build {
    /* Mobile: tighter, fewer characters needed when space is at a premium */
    font-size: 8px;
    padding: 1px 4px;
  }
}

/* Play surface — fills viewport under top bar */
.play-surface {
  position: absolute;
  top: clamp(48px, 4.5vh, 64px);
  left: 0; right: 0; bottom: 0;
  overflow: hidden;
  contain: layout style;
  /* single source of truth for hand height — pill zones derive bottom from this.
     Bumped 1.44 → 1.65 → 1.75 multiplier (and 24 → 30 add) to match the
     arc-fan vertical extent in .hand (components.css). A 20°-rotated
     card with origin 50% 100% occupies ~1.52 × cw vertically plus the
     center-card apex lift; at radius 280 that apex grows to ~17px, so
     we round up to 1.75 × cw + 30px so the hand-zone never clips the
     rotated corners or the deepened apex card. Keep this in lockstep
     with the .hand height in components.css. */
  --hand-zone-h: calc(var(--hand-cw) * 1.75 + 30px + clamp(8px, 1.5vh, 20px));

  /* Sprint 12 — vertical-rhythm tokens for the south-anchored stack.
     The bottom-of-screen UI stacks like this, low to high:
         screen edge
         ↓  var(--hand-lift, 0px)         — thumb-comfort lift
         hand-zone (cards arc)
         ↓  var(--rhythm-banner-clear)    — reserved row for a BRB /
                                            spectator / idle / waiting
                                            banner. 0 when no banner
                                            shows; non-zero when one
                                            does, pushing the south
                                            stack up by exactly the
                                            banner's height + a 12px
                                            min gap so the banner can
                                            never collide with the
                                            south chip above. Gated
                                            on data-has-banner="true".
         ↓  var(--rhythm-hand-to-south)   — empty space above hand top
                                            (above the banner row when
                                            shown)
         south chip (Keith's seat pill)
         ↓  var(--rhythm-south-chip-h) + var(--rhythm-south-to-bid)
                                          — chip occupies this much,
                                            then a small gap above it
         bid-controls-zone (Nil/1..13 grid)
     Each downstream rule sums all the gaps + heights BELOW it. Adding
     a new logical layer means declaring its gap + height token and
     updating only the rules above that layer.
     Default values here reflect the legacy desktop layout; the mobile
     breakpoint overrides them for the larger phone cards / pill. */
  /* Sprint 14 Phase G user-tweak (Frodo padding-fix verdict, 2nd pass):
     pull the south chip + spectator banner DOWN toward the player
     hand to close the trick-zone↔pill vacuum. Was 24px desktop;
     now adaptive clamp(6,0.8vh,12). The chip's autoplay-firing
     hairline is preserved by the floor (6px) + the existing
     hand-zone-h arc cushion. Mobile override below. */
  --rhythm-hand-to-south: clamp(6px, 0.8vh, 12px);
  --rhythm-south-chip-h:  32px;
  --rhythm-south-to-bid:  16px;
  --rhythm-banner-clear:  0px;
  /* Sprint 14 Phase G user-tweak — symmetric pad above and below a
     showing banner so the "You're spectating" pill is equally spaced
     between the hand-top and the south chip bottom. Adaptive via
     clamp so the pad grows on taller viewports. */
  --rhythm-banner-pad: clamp(12px, 2.2vh, 22px);
}

/* When a banner is showing (spectator / BRB / idle / waiting), reserve
   a row above the hand-zone so the banner sits in dedicated space and
   the south chip bumps up by the same amount. The banner-clear value
   is computed so that:
     chip-bottom = hand-lift + hand-zone-h + 2*banner-pad + 36px
   i.e. banner-pad gap below banner, 36px banner height, banner-pad
   gap above banner. The cascade adds banner-clear + rhythm-hand-to-
   south so:  banner-clear = 2*banner-pad + 36 - rhythm-hand-to-south.
   This makes the banner symmetrically padded between cards and chip,
   adaptively at any viewport. */
.play-surface[data-has-banner="true"] {
  --rhythm-banner-clear: calc((var(--rhythm-banner-pad) * 2) + 36px - var(--rhythm-hand-to-south));
}

/* Sprint 14 — trick-zone vertical centering between the top (north)
   and bottom (south) name pills. The trick zone, east pill and west
   pill all share this y so the diamond layout stays coherent as the
   hand/banner geometry changes underneath.

   Derivation:
     north_pill_top    = clamp(70px,7vw,200px)         (from top)
     south_pill_bottom = hand-lift + hand-zone-h
                       + banner-clear + rhythm-hand-to-south  (from bottom)
   Free space between the pills, measured from play-surface-top:
     free_top    = north_pill_top + pillHeight
     free_bottom = play-surface-height − south_pill_bottom − pillHeight
   Midpoint (pill heights cancel):
     mid = (north_pill_top + 100% − south_pill_bottom) / 2
         = 50% + (north_pill_top − south_pill_bottom) / 2
   East/West pills use the same y so they continue to read as flanking
   the trick zone visually. */
.play-surface {
  --north-pill-top: clamp(70px, 7vw, 200px);
  --south-pill-bottom: calc(
    var(--hand-lift, 0px)
    + var(--hand-zone-h)
    + var(--rhythm-banner-clear, 0px)
    + var(--rhythm-hand-to-south)
  );
  --trick-center-y: calc(50% + (var(--north-pill-top) - var(--south-pill-bottom)) / 2);
}

/* Sprint 12 — thumb-comfort lift.
   On a touch device held in portrait, the hand pinned to the screen
   edge is awkward to tap one-handed — thumbs reach comfortably to the
   lower-mid band, not the very bottom. When the table is in a
   headroom-bearing phase (bidding / lobby / roundOver / gameOver —
   the center trick zone is empty), lift the hand-zone, bid-pill-zone,
   and BRB-banner-zone together so the spatial relationship between
   cards and bid pill stays constant; only the whole stack moves up
   into the thumb arc.
     - `pointer: coarse` + `hover: none` gates to touch devices only
       (laptops/desktop with a mouse keep the hand at the edge).
     - Portrait + min-height 720px excludes pocket phones with no
       headroom and rotated landscape (where the bottom IS the thumb).
     - `playing` phase opts out because the trick zone is active and
       the cards belong at the baseline.
     - Lift is clamped 40–80px — far enough to be reachable, short
       enough to never crowd opponent seats or the trick zone. */
@media (hover: none) and (pointer: coarse)
   and (orientation: portrait) and (min-height: 720px) {
  .play-surface[data-phase="bidding"],
  .play-surface[data-phase="lobby"],
  .play-surface[data-phase="roundOver"],
  .play-surface[data-phase="gameOver"],
  /* Spectator-all-bots: the viewer's "hand" is a read-only preview of
     the seat they're watching — they never tap a card, so the
     "playing-phase needs cards at the baseline for tapping" rationale
     doesn't apply. Lift in every phase (including `playing`) so the
     hand doesn't crowd against the corner Reset/Stop buttons and
     spectating banner, and stays comfortably above the iOS home
     indicator. */
  .play-surface[data-spectator-all-bots="true"] {
    /* 9vh scales naturally with available real estate:
         iPhone 13 (844)  → 76px
         iPhone Pro Max (932) → 84px
         iPad portrait (1024) → 92px
         iPad Pro 12.9 (1366) → capped at 130px
       40px floor catches the 720px trigger boundary; 130px ceiling
       prevents tablets in portrait from over-lifting into the opponent
       seat row. */
    --hand-lift: clamp(40px, 9vh, 130px);
  }
}

/* Felt shell */
.table-shell {
  position: absolute;
  inset: 0;
  background: var(--bg);
  contain: layout style;
}

.table-shell__felt {
  position: absolute;
  top: 8%; bottom: 8%;
  left: 5%; right: 5%;
  background: var(--felt-mid);
  border-radius: var(--radius-felt);
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.1), 0 2px 8px rgba(0,0,0,0.1);
}

/* Seat anchors — desktop */
.seat {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
}

.seat--north {
  /* vw couples to felt geometry (felt top = 8% of height ≈ 6.4vw at 16:9) */
  top: clamp(70px, 7vw, 200px);
  left: 50%;
  transform: translateX(-50%);
  flex-direction: column;
  gap: 10px;
}

.seat--west {
  left: clamp(70px, 5vw, 200px);
  /* Sprint 14 — share the trick-zone vertical center so the diamond
     layout stays balanced as the south rhythm shifts. */
  top: var(--trick-center-y);
  transform: translateY(-50%);
  flex-direction: row;
  gap: 14px;
}

.seat--east {
  right: clamp(70px, 5vw, 200px);
  top: var(--trick-center-y);
  transform: translateY(-50%);
  flex-direction: row-reverse;
  gap: 14px;
}

/* South seat chip — first layer above the hand. Cascade:
   bottom = hand-lift + hand-zone-h + (banner clearance) + (gap above hand) */
.seat--south {
  bottom: calc(
    var(--hand-lift, 0px)
    + var(--hand-zone-h)
    + var(--rhythm-banner-clear, 0px)
    + var(--rhythm-hand-to-south)
  );
  z-index: 6;
  isolation: isolate;
  left: 0; right: 0;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

/* Score / info rails (desktop only) */
.rail {
  position: absolute;
  top: clamp(60px, 5vh, 90px);
  width: clamp(160px, 14vw, 260px);
  display: flex;
  flex-direction: column;
  gap: clamp(8px, 0.8vw, 14px);
}

.rail--left  { left: clamp(12px, 1.4vw, 28px); }
.rail--right { right: clamp(12px, 1.4vw, 28px); }

/* Trick zone — centered in the FREE space between the top (north) and
   bottom (south) name pills. Uses --trick-center-y defined on
   .play-surface, which derives the midpoint analytically from
   --north-pill-top and --south-pill-bottom. East and west pills
   anchor to the same var so they flank the trick visually. */
.trick-zone {
  /* trick cards = 80% of hand cards; container sized to exactly contain the cross + 16px margin */
  --cw: calc(var(--hand-cw) * 0.8);
  --tz-size: calc(var(--hand-cw) * 2.5);
  position: absolute;
  top: var(--trick-center-y);
  left: 50%;
  transform: translate(-50%, -50%);
  width: var(--tz-size);
  height: var(--tz-size);
}

.trick-zone__slot {
  position: absolute;
}

.trick-zone__slot--south {
  bottom: 0; left: 50%;
  transform: translateX(-50%);
}

.trick-zone__slot--west {
  left: 0; top: 50%;
  transform: translateY(-50%) rotate(-90deg);
}

.trick-zone__slot--north {
  top: 0; left: 50%;
  transform: translateX(-50%);
}

.trick-zone__slot--east {
  right: 0; top: 50%;
  transform: translateY(-50%) rotate(90deg);
}

/* Hand zone — anchored at bottom of play surface */
.hand-zone {
  position: absolute;
  bottom: var(--hand-lift, 0px);
  left: 0;
  right: 0;
  height: var(--hand-zone-h);
  display: flex;
  justify-content: center;
  align-items: flex-end;
  /* Sprint 14 Phase G user-tweak — cards a tiny bit higher,
     adaptively. align-items: flex-end pins the arc to the bottom of
     the zone; bumping padding-bottom lifts the cards uniformly. Was
     clamp(8,1.5vh,20); raised the floor + slope so the lift scales
     with viewport height (8→12 small, 20→26 large). */
  padding-bottom: clamp(12px, 2.0vh, 26px);
  pointer-events: none; /* let clicks through to the table; hand itself re-enables */
  z-index: 5;
  isolation: isolate; /* seals transform-lifted card SC from compositor-layer escape vs z=7 pill */
}

.hand-zone > * { pointer-events: auto; }

/* Bid-controls zone — sits one layer above the south chip. Cascade:
   bottom = hand-lift + hand-zone-h + (gap above hand)
          + (south chip height) + (gap above south chip)
   Equivalent to the legacy `+80` on desktop (32 + 32 + 16) and `+130`
   on mobile (60 + 40 + 30) — but the addends are now named, so a
   designer tweak to any one gap propagates through the cascade
   without manual recomputation. */
.bid-controls-zone {
  position: absolute;
  bottom: calc(
    var(--hand-lift, 0px)
    + var(--hand-zone-h)
    + var(--rhythm-banner-clear, 0px)
    + var(--rhythm-hand-to-south)
    + var(--rhythm-south-chip-h)
    + var(--rhythm-south-to-bid)
  );
  left: 0; right: 0;
  display: flex;
  justify-content: center;
  pointer-events: none;
  z-index: 7;
  isolation: isolate; /* paired with hand-zone isolation: seals pill SC from compositor-layer ordering quirks */
}

.bid-controls-zone > * { pointer-events: auto; }

/* Banner zone — sibling of hand-zone / bid-controls-zone so the BRB /
   idle / waiting / spectator banners share a dedicated stacking
   context above the bid pill (z=7) and the hand cards (z=5). Fixes
   the "I literally can't press I'm here?" bug: when banners lived
   inside .hand-zone, their z-index: 10 was relative to .hand-zone's
   z=5 stacking context (due to isolation: isolate), so the
   .bid-controls-zone at z=7 painted over them and stole the click. */
.brb-banner-zone {
  position: absolute;
  bottom: var(--hand-lift, 0px);
  left: 0;
  right: 0;
  height: var(--hand-zone-h);
  pointer-events: none;
  z-index: 12;
  /* SCR5-WARN-4 — banner content geometrically overflows the zone via
     `bottom: calc(var(--hand-zone-h) + 12px)` (components.css). Stay
     `overflow: visible` so the banner isn't clipped, and DO NOT add
     `isolation`/`transform` here — those would create a stacking
     context that breaks z=12 escape into the page-level layer. */
  overflow: visible;
}

.brb-banner-zone > * { pointer-events: auto; }


/* ── Mobile ≤ 559px ─────────────────────────────────────────────────────────── */
@media (max-width: 559px) {
  /* Bigger cards on phones, but keep the desktop 0.7 overlap so the corner
     "10" glyph isn't clipped by the next card. Width tuned so the 13-card
     fan still clears (100% − 32px) at 360-559px viewports.
     Floor kept at 60 so 13-card hand fits at 320px viewport
     (60 + 12 × 60 × 0.3 = 276 ≤ 288 = 320−32). Plateau bumped a bit
     to match the desktop bump (92→100) for legibility on 5"+ phones. */
  :root {
    --hand-cw: clamp(60px, 18vw, 100px);
  }

  /* Mobile layout band-budget (vertical):
       - North pill near top
       - Trick-zone centered (empty during bidding)
       - East/West pills at center, but at the LEFT/RIGHT *sides* —
         they don't compete horizontally with the centered bid panel
       - South pill above the hand
       - Hand at the bottom; cards translate up to -48px during the
         autoplay-firing animation
     Two constraints we tune below:
       (a) South pill must clear cards even when cards lift -48px
       (b) Bid panel must clear south pill top by enough to fit a
           wrapped 2-row bid grid + optional Blind Nil row above it */

  /* Mobile rhythm-token overrides.
     The position rules on .seat--south and .bid-controls-zone now
     read directly from these tokens — no per-element bottom-rule
     override needed at this breakpoint.
       hand-to-south 60: cards have bigger transforms (selected -22,
         autoplay -32, autoplay-firing -48) so we need ~30px headroom
         in the worst case.
       south-chip-h 40: phone-sized chip is a bit taller (bigger
         avatar + name + bid badge cluster).
       south-to-bid 30: room for the larger 2-row bid grid + Blind
         Nil row to render without crowding the chip from below.
     Total south rise = 60 (vs 32 desktop).
     Total bid rise = 60 + 40 + 30 = 130 (vs 80 desktop). */
  .play-surface {
    /* Sprint 14 Phase G user-tweak (Frodo padding-fix verdict, 2nd pass):
       pull the south chip + spectator banner DOWN on mobile too —
       the trick zone is currently touching the chip on the screenshot
       the user shared. Was 48px; now adaptive clamp(10,1.6vh,20).
       Paired with the desktop change in the root .play-surface block.
       On a 932px viewport this drops the chip ~33px, clearing the
       trick-to-chip vacuum without moving the hand cards (which are
       intentionally high for thumb tap). */
    --rhythm-hand-to-south: clamp(10px, 1.6vh, 20px);
    --rhythm-south-chip-h: 40px;
    --rhythm-south-to-bid: 30px;
  }

  /* Hand-zone bottom padding — modest thumb-rest plus iOS safe-area.
     Kept small (no card-based add) because growing it pushes cards
     up into the south pill on autoplay-firing. */
  .hand-zone {
    /* Sprint 14 Phase G user-tweak — cards higher on mobile too. Bumped
       to match the desktop clamp(12,2.0vh,26) while preserving the iOS
       safe-area add. */
    padding-bottom: calc(clamp(12px, 2.0vh, 26px) + env(safe-area-inset-bottom, 0px));
  }

  .top-bar__menu-btn {
    width: 30px; height: 30px;
    border-radius: 8px;
    background: rgba(0,0,0,0.05);
    font-size: 14px;
    padding: 0;
    display: flex; align-items: center; justify-content: center;
  }

  .seat--north {
    gap: 6px;
  }

  .seat--west {
    left: 8px;
    /* Inherits --trick-center-y from .play-surface — Sprint 14 unifies
       east+west+trick-zone on the same vertical center so the diamond
       layout stays coherent. The pre-Sprint-14 hardcoded `top: 34%`
       was an approximation of the right value; the variable now
       computes it exactly per geometry. */
    top: var(--trick-center-y);
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
  }

  .seat--east {
    right: 8px;
    top: var(--trick-center-y);
    flex-direction: column;
    align-items: flex-end;
    gap: 6px;
  }

  .seat--south {
    gap: 8px;
  }

  .rail { display: none; }
}

/* ── Tablet 560–899px ─────────────────────────────────────────────────────── */
/* Intentionally thin: seat clamps floor correctly via their own min values;
   desktop geometry is retained and rail panels hide. No extra overrides needed.
   Cutover dropped from 1023→899 so corner score panels persist down to
   small-laptop widths (~900-1023px) instead of jumping to the centered
   chip prematurely. */
@media (min-width: 560px) and (max-width: 899px) {
  .rail { display: none; }
}

