/* ==========================================================================
   blackjack.css — betting and the Blackjack table.
   A separate implementation from table.css's Dou Di Zhu felt (one player,
   one or more hands, no seats), so every class here is its own; nothing is
   shared with .table/.hand/.seat/.play. Reads only the variables in
   tokens.css, and reuses app.css's generic .page/.tile/.btn/.card-panel
   scaffolding for the mode and table-picker screens the same way every other
   screen in the app does. Betting itself lives on the felt (below), not a
   separate page — the action row holds the bet controls when there is no
   round in progress, the same slot Hit/Stand/Double live in once there is.
   ========================================================================== */

/* --- chips, reused for the bet selector both before the first deal and
   again in the action row once a round settles --------------------------- */

.bj-chip-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.bj-chip {
  border-radius: var(--r-pill); min-width: 64px; text-align: center;
  border-color: rgba(205, 174, 108, 0.5);
}

/* --- the rules panel: an overlay, since it is opened from both betting and
   mid-round and does not need to reserve layout space either way --------- */

.bj-overlay {
  position: fixed; inset: 0; z-index: var(--z-overlay); display: grid; place-items: center; padding: 18px;
  background: rgba(3, 12, 9, 0.78); backdrop-filter: blur(3px);
}
.bj-rules {
  width: min(92vw, 560px); max-height: 84vh; overflow-y: auto; padding: 24px 26px;
  border-radius: var(--r-lg); border: 1px solid rgba(205, 174, 108, 0.4);
  background: linear-gradient(170deg, #123a2d, #07241b); box-shadow: var(--shadow-8);
}
.bj-rules h2 { margin: 0 0 12px; font-size: clamp(20px, 3.4vw, 26px); color: var(--ivory); }
.bj-rules ul { margin: 0 0 18px; padding-left: 20px; line-height: 1.7; color: var(--text-secondary); font-size: 15px; }
.bj-rules li { margin-bottom: 8px; }

/* --- the table -------------------------------------------------------------
   --card-w lives here, same rule table.css follows for .table: declared once
   on the root so every descendant inherits it, overridden only on the cards
   themselves (never with a percentage margin — it would resolve against the
   row, not the card, and a 4-hand split or a 5-card Malaysian hand would
   collapse into a pile as it grew). Sized off viewport height rather than
   width: a hand's size is about legibility on a short phone screen, not
   about how wide the table is. */

.bj-table {
  position: fixed; inset: 0; display: flex; flex-direction: column; overflow: hidden;
  --card-w: 20vh;
  background:
    radial-gradient(60% 50% at 50% 38%, rgba(255, 255, 255, 0.08), transparent 70%),
    radial-gradient(120% 100% at 50% 120%, rgba(0, 0, 0, 0.55), transparent 60%),
    linear-gradient(170deg, var(--felt-table), #0c3326 55%, #071c15);
}

.bj-hud {
  flex: 0 0 auto; display: flex; align-items: center; flex-wrap: wrap; gap: clamp(6px, 1.4vw, 14px);
  padding: 7px clamp(10px, 2vw, 22px);
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.08));
  border-bottom: 1px solid var(--hairline);
  font-size: clamp(12px, 1.6vw, 15px);
}
.bj-hud__title { color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bj-hud__spacer { flex: 1; }
.bj-hud__pill {
  display: flex; align-items: baseline; gap: 6px; white-space: nowrap;
  padding: 5px 11px; border-radius: var(--r-pill);
  border: 1px solid var(--hairline-strong); background: rgba(0, 0, 0, 0.3);
}
.bj-hud__pill small { font-size: 0.8em; letter-spacing: 0.13em; text-transform: uppercase; color: var(--text-tertiary); }
.bj-hud__pill b { font-size: 1.15em; color: var(--gold-bright); font-variant-numeric: lining-nums tabular-nums; }

.bj-felt {
  flex: 1; min-height: 0; position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: space-between;
  gap: clamp(4px, 1vh, 10px); padding: clamp(4px, 1vh, 10px) 10px;
}

.bj-shoe { position: absolute; top: 6px; right: clamp(8px, 2vw, 20px); --card-w: clamp(26px, 3.6vw, 38px); opacity: 0.92; }

.bj-hint { color: var(--text-tertiary); font-size: clamp(13px, 1.8vw, 16px); text-align: center; padding: 8px; }

.bj-dealer { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.bj-dealer__row { display: flex; align-items: baseline; gap: 10px; }
.bj-dealer__name { font-size: var(--t-label); letter-spacing: var(--track-label); text-transform: uppercase; color: var(--text-tertiary); }
.bj-dealer__total { font-size: clamp(14px, 2.2vw, 19px); color: var(--gold-bright); min-height: 1.2em; }
.bj-dealer__cards { display: flex; justify-content: center; min-height: calc(var(--card-w) * 1.4); }
/* The cards' own shadow (the base .card rule, table.css) is enough — no
   extra box-shadow here, which used to draw a second, harder-edged rectangle
   behind the fan on the felt's dark background. */
.bj-dealer__cards .card { margin-left: calc(var(--card-w) * -0.42); }
.bj-dealer__cards .card:first-child { margin-left: 0; }

.bj-hands { display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-end; gap: clamp(8px, 1.8vw, 18px); max-width: 100%; }
.bj-hand {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 6px 10px; border-radius: var(--r-md); border: 1px solid transparent;
  transition: border-color var(--dur-med) var(--ease-std), box-shadow var(--dur-med) var(--ease-std);
}
/* Only ever applied with more than one hand on the felt (see paintHands) —
   with a single hand there is nothing for a ring to distinguish it from. */
.bj-hand.is-active { border-color: var(--gold-bright); box-shadow: 0 0 0 2px rgba(239, 219, 166, 0.28); }
.bj-hand__cards { display: flex; min-height: calc(var(--card-w) * 1.4); }
.bj-hand__cards .card { margin-left: calc(var(--card-w) * -0.38); }
.bj-hand__cards .card:first-child { margin-left: 0; }
.bj-hand__foot { display: flex; gap: 10px; font-size: clamp(12px, 1.6vw, 14.5px); }
.bj-hand__total { color: var(--gold); font-variant-numeric: lining-nums tabular-nums; }
.bj-hand__bet { color: var(--text-tertiary); }
.bj-hand__result { font-size: clamp(11px, 1.4vw, 13px); color: var(--text-secondary); }

/* --- the merged Malaysian table: one banker box (top) and four seats (a row
   below it), with the human occupying exactly one of the five — the swap
   button between them moves which one. Every box shares one set of classes
   (.bj-box*); "you" (.bj-box--you) is the only one ever sized up to the
   >=20%-of-viewport-height floor the spec sets for your own hand — a seat
   never carries that modifier except while a human sits there, so the small
   11vh size below is otherwise always safe for the row's width budget. ---- */

/* flex:1 (not .bj-felt's own justify-content:space-between) claims the
   felt's spare vertical space and centres the banker box, the swap button
   and the seat row as one group in it — same technique the old
   .bj-dealerseat-rows used, just generalised to a third child (the swap
   button) between the other two. */
.bj-mtable {
  flex: 1; min-height: 0; width: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(10px, 2.6vh, 26px);
}

.bj-seatrow { display: flex; justify-content: center; align-items: flex-start; gap: clamp(4px, 1.4vw, 16px); width: 100%; }
/* Every seat the same width regardless of content ("×" vs "+ Bot" vs a
   three-digit bet), and it stays that way as a round plays out — flex:1 1 0
   (not the box's own default 0 1 auto) makes all four share the row equally
   by construction rather than each sizing to its own label. */
.bj-seatrow > .bj-box { flex: 1 1 0; }

.bj-box {
  flex: 0 1 auto; min-width: 0;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 5px 6px; border-radius: var(--r-md); border: 1px solid var(--hairline-strong);
  background: rgba(0, 0, 0, 0.22); font: inherit; color: inherit;
  /* A real touchstart on a seat must never be swallowed by Chromium's
     default double-tap-to-zoom delay. */
  touch-action: manipulation;
  transition: border-color var(--dur-med) var(--ease-std), box-shadow var(--dur-med) var(--ease-std);
}
.bj-box:disabled { cursor: default; }
button.bj-box:not(:disabled) { cursor: pointer; }
/* The human's own box, wherever it is — gold ring, same affordance weight as
   .bj-box.is-openable/.is-active below use for their own moments. */
.bj-box--you { border-color: var(--gold-bright); box-shadow: 0 0 0 2px rgba(239, 219, 166, 0.35); }
/* Tappable only once the banker is at 16+ and this seat is still pending
   (see enableOpenableMSeats in ui/blackjack.js). */
.bj-box.is-openable { cursor: pointer; border-color: var(--gold-bright); box-shadow: 0 0 0 2px rgba(239, 219, 166, 0.28); }
/* Whose turn it is, seat or banker — a softer ring than "you"/"openable" so
   the two never fight for attention when the active seat is also yours. */
.bj-box.is-active { border-color: #7fd1a8; box-shadow: 0 0 0 2px rgba(127, 209, 168, 0.32); }

.bj-box__row { display: flex; align-items: baseline; gap: 6px; max-width: 100%; }
.bj-box__name { font-size: clamp(11px, 1.4vw, 13px); color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.bj-box--you .bj-box__name { color: var(--gold-bright); }
.bj-box__bet { font-size: clamp(11px, 1.4vw, 13px); color: var(--gold-bright); font-variant-numeric: lining-nums tabular-nums; }

/* 11vh everywhere, phone sizes included, capped so an unusually tall window
   never lets a 5-card fan outgrow the row's width budget. The cap has to
   clear >=15% of the viewport height at 1280x720 (PR #18's own floor for
   these boxes) — 84px (84*1.4=117.6px, 16.3% of 720) does, with 11vh itself
   resolving under the cap at every viewport this table is tested at, so the
   two landscape queries below only need to touch the surrounding chrome,
   never this. */
.bj-box__cards { --card-w: clamp(30px, 11vh, 84px); display: flex; min-height: calc(var(--card-w) * 1.4); align-items: center; justify-content: center; }
.bj-box__cards .card { margin-left: calc(var(--card-w) * -0.5); }
.bj-box__cards .card:first-child { margin-left: 0; }
/* Your own box: the same >=20vh floor the single-hand table always used —
   capped for the same reason as the small size above, just at a size a
   5-card hand still clears the spec's percentage floors comfortably at
   (see the measured table in the PR description). */
.bj-box--you .bj-box__cards { --card-w: clamp(56px, 20vh, 110px); }

.bj-box__total { font-size: clamp(11px, 1.4vw, 13px); color: var(--gold-bright); min-height: 1.2em; font-variant-numeric: lining-nums tabular-nums; }
.bj-box__result { font-size: clamp(10.5px, 1.3vw, 12.5px); min-height: 1.2em; text-align: center; }
.bj-box__result.is-win { color: var(--ok); }
.bj-box__result.is-lose { color: var(--danger); }
.bj-box__result.is-push { color: var(--text-secondary); }

/* Swap's own row: holds just the swap button between rounds, and the result
   banner beside it once a round settles (showMBanner/restoreSwapSlot) — a
   real flex sibling, never an overlay, so the two can never overlap and
   swap never has to leave the DOM (and stop being tappable) to make room
   for the banner. gap >=8px is the same clearance the banner needs from
   every box, just spent here as the row's own gap instead of a margin. */
.bj-midrow { display: flex; align-items: center; justify-content: center; gap: clamp(8px, 2vw, 16px); }

/* --- swap: moves the human between the banker box and seat 1, between
   rounds only. A circular icon badge (the reversal glyph) plus a caption —
   not a bare icon button — so "Swap" reads without relying on an emoji
   font. ----------------------------------------------------------------- */
.bj-swap {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  background: none; border: 0; padding: 2px; font: inherit; color: var(--text-tertiary);
  touch-action: manipulation;
}
.bj-swap:not(:disabled) { cursor: pointer; }
.bj-swap:disabled { opacity: 0.45; cursor: default; }
.bj-swap__icon {
  width: clamp(32px, 5vh, 42px); height: clamp(32px, 5vh, 42px); border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--hairline-strong); background: rgba(0, 0, 0, 0.32); color: var(--gold-bright);
  transition: border-color var(--dur-fast) var(--ease-std), transform var(--dur-fast) var(--ease-std);
}
.bj-swap__icon svg { width: 55%; height: 55%; }
.bj-swap:not(:disabled):hover .bj-swap__icon { border-color: var(--gold-bright); }
.bj-swap:not(:disabled):active .bj-swap__icon { transform: scale(0.92); }
.bj-swap__label { font-size: clamp(10px, 1.3vw, 12px); letter-spacing: 0.08em; text-transform: uppercase; }
/* A blocked reason (bankroll, mid-round) has to be readable on a touch
   screen that gets no hover/title tooltip at all — a flat 12px floor, not
   the same vw-clamp the caption uses, which resolves under 12px at every
   width this table is tested at. Shown/hidden from JS (paintMSwap), not
   CSS, so it costs no space at all the rest of the time, the common case. */
.bj-swap__note { font-size: 12px; line-height: 1.15; color: var(--text-tertiary); max-width: 22ch; text-align: center; }

/* position + z-index: .bj-felt is itself position:relative (it anchors the
   banner), which alone would let its content paint above this non-positioned
   sibling if the felt's rows ever ran taller than its box — the buttons here
   must never be a hit-testing casualty of that. */
.bj-actions { position: relative; z-index: 1; flex: 0 0 auto; display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: clamp(8px, 1.6vw, 14px); padding: 6px 10px clamp(8px, 1.4vh, 14px); min-height: 40px; }
.bj-actions .btn { padding: 9px 16px; font-size: clamp(14px, 1.8vw, 16px); }

/* --- betting controls: bet readout, chips, Clear, Repeat bet and Deal, all
   one row in the action row, whenever there is no round in progress. One row
   (not stacked) so the action bar stays as short as it is during a round —
   see the comment on .bj-actions and paintBettingControls for why that
   matters to the result banner. --------------------------------------------- */

.bj-bet-live { display: flex; align-items: baseline; gap: 6px; font-size: clamp(13px, 1.6vw, 15px); color: var(--text-secondary); }
.bj-bet-live b { font-size: 1.2em; color: var(--gold-bright); font-variant-numeric: lining-nums tabular-nums; }

.bj-insurance {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  padding: 12px 18px; border-radius: var(--r-lg); border: 1px solid var(--hairline-strong);
  background: rgba(6, 22, 17, 0.9); text-align: center; font-size: 14.5px; color: var(--text-secondary);
}

/* --- the result banner: over the felt, not a modal. Both hands stay fully
   visible underneath it, and it never intercepts a click — the betting
   controls for the next round are already live below it. `top` is set in JS
   (showBanner) to the measured midpoint between the dealer's cards and the
   player's, not centered in the felt as a whole: with two rows of cards each
   a quarter of the screen tall, the felt's own centre often lands inside one
   of them rather than the gap between. */

.bj-banner-host { position: absolute; inset: 0; z-index: 5; pointer-events: none; }
.bj-banner {
  position: absolute; left: 50%; top: 50%; pointer-events: none; text-align: center; padding: 6px 20px 8px;
  border-radius: var(--r-lg); border: 1px solid rgba(205, 174, 108, 0.5);
  background: rgba(5, 18, 13, 0.86); box-shadow: var(--shadow-8);
  opacity: 0; transform: translate(-50%, -50%) scale(0.92);
  transition: opacity var(--dur-med) var(--ease-std), transform var(--dur-med) var(--ease-std);
}
.bj-banner.is-on { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.bj-banner__main { display: flex; align-items: baseline; justify-content: center; gap: 10px; white-space: nowrap; }
.bj-banner__title { font-size: clamp(16px, 2.4vw, 24px); }
.bj-banner.is-win .bj-banner__title { color: var(--gold-bright); }
.bj-banner.is-loss .bj-banner__title { color: #f0a58f; }
.bj-banner.is-push .bj-banner__title { color: var(--text-secondary); }
.bj-banner__delta { font-size: clamp(13px, 1.8vw, 18px); font-variant-numeric: lining-nums tabular-nums; }
.bj-banner.is-win .bj-banner__delta { color: var(--ok); }
.bj-banner.is-loss .bj-banner__delta { color: var(--danger); }
.bj-banner.is-push .bj-banner__delta { color: var(--text-secondary); }
.bj-banner__reason { font-size: clamp(11px, 1.4vw, 13px); color: var(--text-secondary); margin-top: 2px; white-space: nowrap; }

/* Malaysian's own result banner (showMBanner/ui/blackjack.js) sits beside
   the swap button in .bj-midrow instead of floating over a measured gap —
   drop the absolute positioning and the centering translate; .bj-midrow's
   own gap clears swap horizontally, and .bj-mtable's align-items:center
   centres the row as a whole. Only the vertical clearance (to the banker
   box above, the seat row below) is this element's own job: .bj-mtable's
   ambient gap alone is as little as 3-4px at the tight phone breakpoints
   (sized for a swap icon, not an >=8px requirement), so an explicit 6px
   margin on top of it clears >=8px at every tested size (measured 9-26px)
   without eating so much of the felt's own budget that the settled state's
   slimmer margins (a single Deal button, not the in-round worst case) turn
   negative. */
.bj-banner--inline { position: static; left: auto; top: auto; margin: 6px 0; transform: scale(0.92); }
.bj-banner--inline.is-on { transform: scale(1); }

.bj-toast {
  position: fixed; top: 14px; left: 50%; z-index: var(--z-overlay);
  padding: 8px 18px; border-radius: var(--r-pill);
  border: 1px solid var(--hairline-strong); background: rgba(0, 0, 0, 0.72); color: var(--ivory);
  opacity: 0; transform: translate(-50%, -8px);
  transition: opacity var(--dur-med) var(--ease-std), transform var(--dur-med) var(--ease-std);
}
.bj-toast.is-on { opacity: 1; transform: translate(-50%, 0); }

.bj-rotate-gate { display: none; }
@media (orientation: portrait) and (max-width: 960px) {
  .bj-rotate-gate {
    position: fixed; inset: 0; z-index: var(--z-rotate-gate); display: grid; place-items: center; text-align: center;
    padding: 30px; background: linear-gradient(170deg, #0f3b2d, #061a14); color: var(--text);
  }
  .bj-rotate-gate__icon { font-size: 46px; margin-bottom: 14px; color: var(--gold); }
  .bj-rotate-gate h2 { margin: 0 0 8px; font-size: 25px; color: var(--ivory); }
  .bj-rotate-gate p { margin: 0; max-width: 34ch; color: var(--text-tertiary); line-height: 1.6; }
}

/* Short landscape windows (a phone with a browser bar): pull the chrome in
   so a four-hand split or a five-card Malaysian hand still clears the hud
   and the action row. American's own --card-w (19vh) still clears 26% of
   the viewport height here — its chrome gives up padding, not the cards.
   Malaysian's own boxes need a second, larger cut (see .bj-mtable/.bj-box
   below): the merged table's three-deep stack (banker, swap, seat row) does
   not fit this height's real hud/action budget at the same card size. */
@media (orientation: landscape) and (max-height: 460px) {
  .bj-table { --card-w: 19vh; }
  .bj-hud { padding: 2px clamp(8px, 2vw, 22px); gap: 6px; font-size: 11.5px; }
  .bj-hud__pill { padding: 2px 8px; }
  .bj-dealer { gap: 1px; }
  .bj-dealer__row { gap: 6px; }
  .bj-dealer__total { font-size: 12px; }
  .bj-felt { padding: 1px 8px; gap: 1px; }
  /* mtable's own three-child stack (banker box, swap, seat row) has to fit
     the felt's real budget here (hud+actions leave less room than the base
     rule's 26vh math assumed) — the gap, the swap caption and the box
     padding are what give up space below, not the card floors. */
  .bj-mtable { gap: clamp(4px, 1vh, 10px); }
  .bj-hands { gap: 6px; }
  .bj-hand { padding: 2px 6px; gap: 1px; }
  .bj-hand__foot { font-size: 11px; gap: 6px; }
  .bj-seatrow { gap: 4px; }
  .bj-box { padding: 2px 4px; gap: 1px; }
  .bj-box__name, .bj-box__bet { font-size: 10.5px; }
  .bj-box__total, .bj-box__result { font-size: 9.5px; }
  /* Still clears the >=20%-of-viewport-height floor at 844x390 (15.5vh*1.4
     = 21.7% of 390) with room for the row to actually fit under the hud and
     the action bar — 19vh (the un-scoped preferred value) was sized before
     the felt's real hud/action budget at this height was measured. */
  .bj-box--you .bj-box__cards { --card-w: clamp(52px, 15.5vh, 100px); }
  /* Icon only: the "Swap" caption is the first thing to give way at this
     height, same as the banner's reason line below — the circular badge
     alone is still a clear, discoverable affordance. The note stays
     (paintMSwap toggles its own display) — it only ever appears for a
     blocked reason, and that has to stay visible even here. */
  .bj-swap__label { display: none; }
  .bj-swap__icon { width: clamp(28px, 4.4vh, 36px); height: clamp(28px, 4.4vh, 36px); }
  .bj-actions { min-height: 28px; padding: 2px 10px 3px; gap: 6px; }
  .bj-actions .btn { padding: 4px 10px; font-size: 12px; }
  .bj-bet-live { font-size: 11px; }
  /* The dealer-to-hand gap is a sliver at this height — the banner keeps
     title and amount, but the reason line is the first thing to go, and the
     tight line-height leaves a couple of px of margin either side of
     showBanner()'s measured midpoint rather than exactly filling the gap. */
  .bj-banner { padding: 3px 12px; line-height: 1.1; }
  .bj-banner__title { font-size: 12px; }
  .bj-banner__delta { font-size: 10px; }
  .bj-banner__reason { display: none; }
}

/* A phone in landscape with the browser's own chrome showing (844x330 is the
   house's own benchmark for this) leaves so little height that the result
   banner needs every spare pixel between the two card rows it can get — one
   more turn of the same screws as the query above, isolated so 844x390 (which
   already clears the banner comfortably) keeps its larger, easier-to-read
   chrome. */
@media (orientation: landscape) and (max-height: 350px) {
  .bj-table { --card-w: 17.6vh; }
  .bj-hud { padding: 1px clamp(6px, 2vw, 22px); gap: 5px; font-size: 10.5px; }
  .bj-hud__pill { padding: 1px 6px; }
  .bj-dealer__row { gap: 4px; }
  .bj-dealer__name { font-size: 9px; }
  .bj-dealer__total { font-size: 10px; }
  .bj-felt { padding: 0 6px; gap: 0; }
  /* One more turn of the same screws as the query above: mtable's stack
     (banker box, icon-only swap, seat row) has to clear the hud and the
     action bar at this height too, measured the same way. */
  .bj-mtable { gap: clamp(3px, 0.8vh, 8px); }
  .bj-hand { padding: 1px 5px; gap: 0; }
  .bj-hand__foot { font-size: 10px; gap: 5px; }
  .bj-seatrow { gap: 3px; }
  /* 0, not 1px: at this exact height the seat row (the your-seat card size
     below) and the banker's own 5-card fan (both fixed-height regardless of
     card count) already claim the felt's full budget — every px of box
     padding is spent keeping the row inside the hud/actions bar rather than
     spent on breathing room. */
  .bj-box { padding: 0 3px; gap: 0; }
  .bj-box__name, .bj-box__bet { font-size: 9.5px; }
  .bj-box__total, .bj-box__result { font-size: 8.5px; }
  /* Still clears the >=18%-of-viewport-height floor at 844x330 (14vh*1.4 =
     19.6% of 330) — 17.6vh (matching --card-w's own reduction above) left
     the row taller than this height's real hud/action budget allows. */
  .bj-box--you .bj-box__cards { --card-w: clamp(48px, 14vh, 90px); }
  .bj-swap__icon { width: clamp(24px, 4vh, 32px); height: clamp(24px, 4vh, 32px); }
  .bj-actions { min-height: 22px; padding: 1px 8px 2px; gap: 5px; }
  .bj-actions .btn { padding: 3px 8px; font-size: 11px; }
  .bj-banner { padding: 3px 12px 4px; }
  .bj-banner__title { font-size: 12px; }
  .bj-banner__delta { font-size: 10px; }
  /* .bj-banner__reason stays display:none, inherited from the query above. */
}

/* --- deal flight: transform and opacity only, so it stays on the compositor */

.bj-flyers { position: fixed; inset: 0; pointer-events: none; z-index: 6; }
/* Flyers are sized to their destination card (ui/blackjack.js), which is now
   a quarter of the screen tall — several times the shoe icon they launch
   from. scale(0.3) on the "from" keyframe below is what keeps a still-queued
   flyer looking like it belongs at the shoe, not a full-size card parked in
   the corner; `both` (not just `forwards`) is what applies that "from" state
   during its own staggered delay, before the animation itself has started. */
.bj-flyer { position: fixed; transform: translate(-50%, -50%); will-change: transform, opacity; }
.bj-flyers.is-flying .bj-flyer {
  animation: bj-deal-out 300ms var(--ease) both;
  animation-delay: calc(var(--i, 0) * 120ms); /* matches STAGGER_MS in ui/blackjack.js */
}
@keyframes bj-deal-out {
  from { transform: translate(-50%, -50%) scale(0.3); opacity: 1; }
  to { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(1); opacity: 1; }
}

/* A card hit in: a small pop rather than a flight, since it starts already
   where it belongs. */
.bj-card-enter { animation: bj-card-enter 220ms var(--ease-std); }
@keyframes bj-card-enter {
  from { opacity: 0; transform: translateY(14px) scale(0.9); }
  to { opacity: 1; transform: none; }
}

/* The hole card: a scaleX flip, with the face swapped in JS at the midpoint
   (see flipHoleCard in ui/blackjack.js) — never a rotateY-with-backface setup,
   which would need a layout-affecting perspective on an ancestor. */
.bj-flip { display: inline-block; }
.bj-flip.is-flipping { animation: bj-flip 360ms var(--ease-std); }
@keyframes bj-flip {
  0%, 100% { transform: scaleX(1); }
  50% { transform: scaleX(0); }
}
