/* cards.css — card fronts and backs. Loaded by the table and the lobby.
 *
 * FERRO's faces are ported from ferro-server's stylesheet (server.js, the .pcard rules). FERRO
 * sizes them in em off a 10px font on a 36px-wide card; here each face sits in a .face box whose
 * font-size is that same share of the card's width (27.78cqw), so every rule keeps its FERRO value
 * and the face scales with whatever size the card is drawn at. */

/* ================= suit colour =================
 * The colour of a suit is a player choice separate from the front, so every design can be played
 * in two or four colours. A front takes part simply by drawing in currentColor; the four variables
 * below are the whole mechanism. `front-four` opts out — its entire face is the suit colour.
 *
 * Two colour is the default and is expressed as a relationship, not a repeat: diamonds follow
 * hearts and clubs follow spades. A front that wants its own ink (FERRO's muted parchment reds)
 * sets --suit-h / --suit-s and the other two follow automatically. */
.card{
  --suit-s:#16181a;
  --suit-h:#e0202a;
  --suit-d:var(--suit-h);
  --suit-c:var(--suit-s);
}
html[data-colours="four"] .card    { --suit-d:#1b4fd8; --suit-c:#0f7a2e; }
html[data-colours="fouralt"] .card { --suit-d:#0f7a2e; --suit-c:#1b4fd8; }
/* a colour swatch shows its own scheme even when the page is set to another */
.swatch[data-colours="two"] .card     { --suit-d:var(--suit-h); --suit-c:var(--suit-s); }
.swatch[data-colours="four"] .card    { --suit-d:#1b4fd8; --suit-c:#0f7a2e; }
.swatch[data-colours="fouralt"] .card { --suit-d:#0f7a2e; --suit-c:#1b4fd8; }

/* 0,3,0 so these beat a plain .card.red anywhere else in the stylesheets */
.card:not(.front-four)[data-suit="S"]{ color:var(--suit-s); }
.card:not(.front-four)[data-suit="H"]{ color:var(--suit-h); }
.card:not(.front-four)[data-suit="D"]{ color:var(--suit-d); }
.card:not(.front-four)[data-suit="C"]{ color:var(--suit-c); }

/* ---------- the card itself, for pages without the table's stylesheet ----------
 * The lobby's deck picker (.cards-pick) and the History page's hand replay (.replay) both draw real
 * cards outside the table, so both need the base card here (v29).
 * :where() keeps these at the lowest weight so every front and back below still wins. */
:where(.cards-pick, .replay) .card{
  position:absolute; border-radius:6px; background:linear-gradient(160deg,#fffdf7,#f3ecdc);
  box-shadow:0 2px 0 rgba(0,0,0,.15), 0 6px 14px rgba(0,0,0,.35);
  font-family:"Bodoni Moda", Didot, Georgia, serif; color:#16130f;
}
:where(.cards-pick, .replay) .card .idx{position:absolute;left:5px;top:3px;line-height:.95;text-align:center;font-weight:700}
:where(.cards-pick, .replay) .card .idx b{display:block;font-size:22px}
:where(.cards-pick, .replay) .card .idx i{display:block;font-style:normal;font-size:15px}
:where(.cards-pick, .replay) .card .pip{position:absolute;left:12px;right:0;top:50%;transform:translateY(-46%);text-align:center;font-size:40px;line-height:1}
:where(.cards-pick, .replay) .card .court{position:absolute;left:21px;right:6px;top:7px;bottom:7px;border:1.5px solid currentColor;border-radius:3px;display:grid;place-items:center;
  background:repeating-linear-gradient(135deg, rgba(0,0,0,.035) 0 3px, transparent 3px 6px)}
:where(.cards-pick, .replay) .card .court span{font-size:28px;font-weight:700;line-height:1}
:where(.cards-pick, .replay) .card .court em{position:absolute;bottom:2px;font-style:normal;font-size:12px}
:where(.cards-pick, .replay) .card.facedown{
  background:repeating-linear-gradient(45deg, rgba(201,164,92,.55) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, rgba(201,164,92,.55) 0 1px, transparent 1px 7px), #8e1b22;
  border:3px solid #f6efe0;
}
/* Oakmont's own front keeps its warmer ink */
.card.front-oakmont{ --suit-s:#16130f; --suit-h:#b3202a; }

.card, .back { container-type: size; }   /* cqw only works INSIDE a card, never on the card's own properties */
.card .face{
  position:absolute; inset:0; font-size:27.78cqw; line-height:1;
  display:flex; align-items:center; justify-content:center;
  font-family:"IBM Plex Mono", ui-monospace, "Courier New", monospace; font-weight:700;
}

/* ---------- FERRO base face (white, bright red) — French, Standard ---------- */
.card.front-french, .card.front-standard { background:#fdfdfb; border:1px solid rgba(0,0,0,.15); }
.card .pcard-corner{position:absolute; display:flex; flex-direction:column; align-items:center; line-height:1; font-size:.62em; font-weight:800;}
.card .pcard-corner-tl{top:.34em; left:.34em;}
.card .pcard-corner-tr{top:.34em; right:.34em;}
.card .pcard-corner-bl{bottom:.34em; left:.34em; transform:rotate(180deg);}
.card .pcard-corner-br{bottom:.34em; right:.34em; transform:rotate(180deg);}
.card .pcard-corner-suit{margin-top:.08em; font-size:.85em;}
.card .pcard-face{position:absolute; left:23%; right:23%; top:13%; bottom:13%;}
.card .pcard-pip{position:absolute; transform:translate(-50%,-50%); line-height:1; font-size:1.02em;}
.card .pcard-pip.inv{transform:translate(-50%,-50%) rotate(180deg);}
.card .pcard-pip.ace{font-size:2.4em;}
/* French courts: ruled panel with the diagonal, a mirrored emblem over the pip either side of it */
.card .pcard-court{position:absolute; left:4.5%; right:4.5%; top:3.5%; bottom:3.5%; overflow:hidden; border-radius:.12em; display:flex; flex-direction:column;
  border:1px solid rgba(0,0,0,.55); box-shadow:inset 0 0 0 .06em rgba(201,162,75,.8);
  background-image:linear-gradient(135deg, transparent calc(50% - .55px), rgba(0,0,0,.45) calc(50% - .55px), rgba(0,0,0,.45) calc(50% + .55px), transparent calc(50% + .55px));}
.card .pcard-court-half{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; line-height:1;}
.card .pcard-court-half.inv{transform:rotate(180deg);}
.card .pcard-court-emblem{font-size:1.15em; color:#c9a24b; line-height:1;}
.card .pcard-court-pip{font-size:.95em; margin-top:.04em;}

/* ---------- Standard (FERRO option 3): two bigger corners, pip faces, court figure ---------- */
.card.front-standard .pcard-corner{font-size:.78em;}
.card.front-standard .pcard-corner-tl{top:.2em; left:.24em;}
.card.front-standard .pcard-corner-br{bottom:.2em; right:.24em;}
.card.front-standard .pcard-face{left:26%; right:26%; top:9%; bottom:9%;}
.card.front-standard .pcard-pip{font-size:.95em;}
/* …but the ace keeps its single big centre pip. The shared `.pcard-pip.ace` rule above carries the
   same specificity as the line before this one, so source order alone was flattening the ace to the
   size of an ordinary pip — every ace in this deck was drawn as if it were a one-pip number card. */
.card.front-standard .pcard-pip.ace{font-size:2.2em;}
.card.front-standard .pcard-court{left:14%; right:14%; top:10%; bottom:6%; align-items:center; justify-content:flex-end;
  background-image:none; border:1px solid rgba(0,0,0,.35); box-shadow:none;}
/* the court panel's wash follows the suit's own ink now, so it works in four colour too */
.card.front-standard .pcard-court{background-color:color-mix(in srgb, currentColor 5%, transparent);}
.card.front-standard .pcard-court-svg{width:100%; height:100%; display:block;}

/* ---------- FERRO classic: parchment, two corners, rank over suit in the middle ---------- */
.card.front-ferro{background:#f2ead8; border:1px solid rgba(0,0,0,.15); --suit-s:#1c2321; --suit-h:#a3312a;}
.card.front-ferro .pcard-corner{font-size:.88em;}
.card.front-ferro .pcard-corner-tl{top:.2em; left:.2em;}
.card.front-ferro .pcard-corner-br{bottom:.2em; right:.2em;}
.card.front-ferro .pcard-corner-suit{margin-top:.1em; font-size:1em;}
.card .pcard-center{display:flex; flex-direction:column; align-items:center; line-height:1;}
.card .pcard-center-rank{font-size:1.18em; font-weight:800;}
.card .pcard-center-suit{font-size:1.73em; margin-top:.1em;}

/* ---------- Belmont: English pattern, drawn from an SVG sprite ----------
 * The sheet paints its own face and edge (so the corners and the border are the deck's, not the
 * div's) and every pip and index in it is currentColor, which is what makes it four-colourable.
 * The sprite itself must stay in the document but out of the way: display:none on the sheet
 * breaks <use> in some engines, so it is hidden by size instead. */
.deck-sprite{position:absolute; width:0; height:0; overflow:hidden; pointer-events:none;}
.card.front-belmont{background:none; border:0; box-shadow:none; --card-face:#fdfdfb; --card-edge:rgba(0,0,0,.4);}
.card.front-belmont .face{font-size:0;}
.card .pcard-sheet{position:absolute; inset:0; width:100%; height:100%; display:block;}

/* ---------- Four-colour (FERRO v60–v62): the face is the suit colour ---------- */
.card.front-four{color:#fff; border:1px solid rgba(255,255,255,.9); border-radius:14% / 10%; background:#1b1c1e;}
.card.front-four[data-suit="H"]{background:#9b2226;}
.card.front-four[data-suit="D"]{background:#2a5fa5;}
.card.front-four[data-suit="S"]{background:#1b1c1e;}
.card.front-four[data-suit="C"]{background:#2b7a4b;}
.card.front-four .pcard-corner{font-size:1.4em; flex-direction:column; align-items:flex-start; gap:0;}
.card.front-four .pcard-corner-tl{top:.06em; left:.12em;}
.card.front-four .pcard-corner-suit{margin-top:.02em; margin-left:.06em; font-size:.75em;}
/* the big number sits in the middle of the card horizontally (Rany, v9); its height stays where FERRO had it */
.card .pcard-big-rank{position:absolute; left:0; right:0; bottom:4%; text-align:center; line-height:.86; font-weight:800; font-size:2.5em; color:#fff; letter-spacing:-.02em;}
.card.front-four.wide-rank .pcard-big-rank{font-size:2.3em; letter-spacing:-.05em;}

/* the table's glow for picked / best cards still shows on the coloured faces */
.card.front-four.sel, .card.front-four.best{border-color:#f0d99a;}

/* ================= backs =================
 * .back = an opponent's small face-down card, .card.facedown = a closed board card.
 * Oxblood (the Oakmont back) is the default in table.css; the others override it here. */
html[data-back="navy"] .back, html[data-back="navy"] .card.facedown, .swatch[data-back="navy"] .card.facedown{   /* FERRO's back */
  background-color:#0d3f7a;
  background-image:repeating-linear-gradient(45deg,rgba(255,255,255,.17) 0 3px,transparent 3px 12px),
                   repeating-linear-gradient(-45deg,rgba(255,255,255,.17) 0 3px,transparent 3px 12px);
  border-color:#e8dcc0;
}
html[data-back="felt"] .back, html[data-back="felt"] .card.facedown, .swatch[data-back="felt"] .card.facedown{
  background:radial-gradient(ellipse at 50% 40%, #2b7a4c, #1d5637 60%, #12402a);
  border-color:#f6efe0; box-shadow:inset 0 0 0 2px #12402a, inset 0 0 0 3px #c9a45c, 0 3px 8px rgba(0,0,0,.45);
}
html[data-back="felt"] .back::after, html[data-back="felt"] .card.facedown::after, .swatch[data-back="felt"] .card.facedown::after{
  content:"O"; position:absolute; inset:0; display:grid; place-items:center;
  font-family:"DM Serif Display", Georgia, serif; font-size:52cqw; color:#c9a45c; opacity:.9;
}
html[data-back="noir"] .back, html[data-back="noir"] .card.facedown, .swatch[data-back="noir"] .card.facedown{
  background:
    radial-gradient(closest-side circle at 50% 50%, rgba(201,164,92,.6) 0 22%, transparent 25%),
    repeating-conic-gradient(from 0deg at 50% 50%, rgba(201,164,92,.32) 0 5deg, transparent 5deg 15deg),
    radial-gradient(ellipse at 50% 50%, #2a2622, #0e0d0c 75%);
  border-color:#c9a45c; box-shadow:inset 0 0 0 2px #0e0d0c, inset 0 0 0 3px rgba(201,164,92,.7), 0 3px 8px rgba(0,0,0,.5);
}
html[data-back="ivory"] .back, html[data-back="ivory"] .card.facedown, .swatch[data-back="ivory"] .card.facedown{
  background:
    repeating-linear-gradient(45deg, rgba(142,27,34,.55) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(-45deg, rgba(142,27,34,.55) 0 1px, transparent 1px 5px),
    #f4ecd8;
  border-color:#fbf6ea; box-shadow:inset 0 0 0 2px #f4ecd8, inset 0 0 0 3px #8e1b22, 0 3px 8px rgba(0,0,0,.45);
}
.back, .card.facedown { position:absolute; }

/* ================= the picker (table Setup tab and lobby) ================= */
.deck-row{display:flex; gap:8px; align-items:flex-end; flex-wrap:wrap}
.deck-row.muted .deck-opt{opacity:.4}
.deck-note{font-size:11px; opacity:.65; align-self:center; max-width:150px; line-height:1.25}
.deck-opt{
  display:flex; flex-direction:column; align-items:center; gap:4px; padding:5px 5px 4px; border-radius:7px;
  background:none; border:1px solid transparent; color:inherit; font:inherit; cursor:pointer;
}
.deck-opt:hover{border-color:rgba(201,164,92,.5)}
.deck-opt[aria-pressed="true"]{border-color:#c9a45c; background:rgba(201,164,92,.14)}
.deck-opt .swatch{position:relative; display:block}
.deck-opt .swatch .card, .deck-opt .swatch .back{position:absolute; top:0; transform-origin:0 0}
.deck-opt small{font-size:12px; font-weight:600; letter-spacing:.06em; white-space:nowrap}

/* a swatch shows its own back even when the page uses another one */
.swatch[data-back="oxblood"] .card.facedown{
  background:repeating-linear-gradient(45deg, rgba(201,164,92,.55) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(-45deg, rgba(201,164,92,.55) 0 1px, transparent 1px 7px), #8e1b22;
  border:3px solid #f6efe0; box-shadow:0 3px 8px rgba(0,0,0,.45);
}
.swatch[data-back="oxblood"] .card.facedown::after{content:none}
.deck-label{font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;opacity:.7;width:38px;align-self:center}

/* ---------- turning a board card over (js/reveal.js, Rany's strip-flip, v35) ----------
   The overlay sits above the felt and is removed the moment the turn is done; the real card
   underneath is already face up, so a dropped frame or a cancelled animation can never leave a card
   showing the wrong side. It takes no pointer events, so it cannot intercept a click on the board.
   It lives in #stage, NOT in #table-layer — the table layer is replaced wholesale on every draw,
   which is why in v35 a turn was destroyed a frame or two after it started (v36). */
.rv-layer{position:absolute; inset:0; pointer-events:none; z-index:6}
.rv-slot{position:absolute; perspective:620px; transform:rotate(var(--rot));
         transform-style:preserve-3d}
/* The handover at the end of a turn. The real card is shown first, underneath, and the overlay is
   faded off it — so the fraction of a pixel the two disagree by is crossfaded instead of jumped.
   "observe how the card changes slightly after flipping" (Rany, v37). */
.rv-slot.rv-done{opacity:0; transition:opacity .16s linear}
@media (prefers-reduced-motion:reduce){ .rv-slot.rv-done{transition:none} }

/* FLAT BEFORE IT FADES (v41.2) — "the picture inside the card is not straight when it lands, then in
   a frame it goes straight" (Rany).
   The overlay is a clone of the card cut into sixteen strips and rasterised inside a perspective; at
   the end of the turn the strips are square in their own arithmetic but the RASTERISATION is not, so
   the artwork sits a hair askew of its own border. What fades out here should be geometrically
   identical to the card underneath it, so at the handover the whole 3D apparatus is thrown away and
   one plain unrotated clone is left in its place: no perspective, no rotation, no strips, no backface
   rules. The slot keeps its own `--rot`, which is the card's real angle on the board and belongs. */
/* Past the halfway point the card is one piece again: the strips go, and the plain card that has been
   turning behind them all along carries the rest of the turn by itself. Its -0.6px of depth existed
   only to keep it behind the slices, so it goes too — otherwise the perspective scales it by a hair at
   the moment the slices disappear, which is a visible twitch in the one frame it matters. */
.rv-slot.rv-solid .rv-strip{display:none}
.rv-slot.rv-solid .rv-base{transform:none}
.rv-slot.rv-flat{perspective:none; transform-style:flat}
.rv-slot.rv-flat .rv-flip{transform:none !important; animation:none !important; transform-style:flat}
.rv-slot.rv-flat .rv-strip{display:none}          /* the slices have done their work */
/* the shade is the light raking across a card in motion; this one has stopped. It is only ~.03 opacity
   by here anyway, but "only just visible" is the whole complaint — a stopped card is lit like the
   cards either side of it, or the difference is what you look at. */
.rv-slot.rv-flat .rv-shade{display:none}
.rv-slot.rv-flat .rv-base{transform:none}
.rv-slot.rv-flat .rv-back{display:none}
.rv-slot.rv-flat .rv-front{transform:none; backface-visibility:visible}
.rv-flip{position:absolute; inset:0; transform-style:preserve-3d}
@keyframes rvFlip{from{transform:rotateY(0)} to{transform:rotateY(180deg)}}

/* The shading that makes a turn read as a turn (v41.2). Deliberately NOT backface-hidden: it is one
   pane over the whole card and it has to shade both halves of the turn. It is clear at both ends, so
   the back it starts on and the face it lands on are the card's own colours and nothing else's — the
   darkening exists only while the card is side-on to you, which is exactly when a real one is dark.
   The faint warm top edge is the felt light catching the card as it comes round. */
.rv-shade{
  position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  transform:translateZ(1.2px);
  background:linear-gradient(168deg, rgba(255,244,216,.22), rgba(0,0,0,0) 34%, rgba(4,3,2,.9));
  opacity:0;
}
@keyframes rvShade{
  0%   {opacity:0}
  22%  {opacity:.30}
  50%  {opacity:.72}
  78%  {opacity:.30}
  100% {opacity:0}
}
@media (prefers-reduced-motion:reduce){ .rv-shade{display:none} }

.rv-strip{position:absolute; top:0; height:100%; transform-style:preserve-3d}
/* the plain card turning just BEHIND the strips: the bow pulls every strip towards the centre, and
   without something solid back there the felt (and the finished face) shows in the gap at each edge
   and through any hairline between two strips — the "not clean" of v35 */
.rv-base{position:absolute; inset:0; transform:translateZ(-.6px); transform-style:preserve-3d}
.rv-face{position:absolute; inset:0; overflow:hidden; backface-visibility:hidden}
.rv-front{transform:rotateY(180deg)}
.rv-face > i{position:absolute; top:0; display:block}

/* THE CARD IN AN ANIMATION IS WRAPPED IN AN <i>, AND <i> IS ITALIC.
 *
 * This is the bug Rany reported four times over three versions — "the image inside the card is not
 * straight, then in a frame it becomes straight" — and every one of my fixes was aimed at geometry,
 * because "not straight" sounded like a shear. It is not a shear. The card's BORDER is perfectly
 * upright in his footage; only the glyphs lean. A transform would have slanted both.
 *
 * Every one of these wrappers is an <i>, chosen as a short generic element to hang layout on, and the
 * browser's own stylesheet says `i { font-style: italic }`. The cloned card face sits inside it and
 * inherits that. The deck's numerals have no italic cut, so the browser SYNTHESISES one by skewing
 * each glyph — which is precisely a rank that leans while the card around it does not. At the handover
 * the real card takes over and its text is upright: the single frame where it "becomes straight".
 *
 * Measured, on the default deck: the overlay's cloned rank computes font-style `italic`, the real
 * card's the same rank computes `normal`.
 *
 * Reset here rather than by swapping the tags, because it must hold for every wrapper of this kind —
 * the flip, the thrown pile and the Cadeau flights all clone real card faces — and because a rule
 * that says WHY outlives a tag change. The clone's own classes still decide everything else; this only
 * removes a style the wrapper was never meant to contribute. */
.rv-face > i, .tw-card, .dl-card, .gf-card{font-style:normal}
/* the card itself, cloned, at its natural size and scaled to however big the board is drawn. It is a
   real .card element rather than a copy of its contents — copying the contents left a front with no
   card under it, which is what the second recording shows (v36.1). */
.rv-art{position:absolute; left:0; top:0; display:block; transform-origin:0 0}
.rv-art > .card{position:absolute; left:0; top:0; margin:0}
/* The real card, while its overlay is turning over it. reveal.js expires this by itself and render.js
   re-applies it on every draw, so a lost layer cannot leave a card invisible.

   OPACITY, not visibility (v37). `visibility:hidden` takes an element out of hit-testing altogether,
   so for as long as a card was being turned over it could not be tapped — the tap fell through to
   the felt and nothing happened. On a desktop that is a fifth of a second and a mouse you can try
   again with; on a tablet it is a second or more, there is no hover, and a card you jab at twice
   with no response is a broken table. At zero opacity the card is just as invisible and is still
   the thing under your finger. */
.rv-hidden{opacity:0}

@media (prefers-reduced-motion:reduce){ .rv-layer{display:none} .rv-hidden{opacity:1} }
html[data-reveal="off"] .rv-layer{display:none}
html[data-reveal="off"] .rv-hidden{opacity:1}

/* ---------- a card being thrown away (js/reveal.js, v37) ----------
   "include in the animation when a card is being thrown from the board or from a player's hand."
   A copy of the card skims from where it was to where the pile has already drawn it. The pile and
   the board underneath are in their finished state the whole time, so a dropped frame just means
   the card was already there. Pointer-inert: it can never eat a click. */
.tw-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
.tw-card{
  position:absolute; display:block; border-radius:6px;
  animation-name:tw-fly; animation-timing-function:cubic-bezier(.3,.75,.35,1); animation-fill-mode:both;
}
.tw-card .rv-art{position:absolute; left:0; top:0; transform-origin:0 0}
.tw-card .card{position:absolute; left:0; top:0; margin:0}
/* THE CARD IS NEVER NOWHERE (Rany, v41.21: "board cards which are up disapear and cards reappear").
   It used to start at opacity 0 and fade in over the first tenth of its flight. That reads fine at
   delay zero and is a hole in the table at any other delay: a garbage card can hold these flights back
   by a second or more (see the wait in reveal.sync), and `animation-fill-mode:both` holds the 0%
   keyframe for the whole wait — so the card left the board, was drawn nowhere at all for a second, and
   then faded in somewhere else. Starting visible, on the exact spot the real card was drawn a frame
   ago, is continuity rather than a pop: the copy simply takes over from the card.
   --s1 is how much bigger or smaller it lands than it started (the board and the pile are drawn at
   different scales); it defaults to 1, at which the 45% value is the 1.06 lift this always had. */
.tw-card{--s1:1}
@keyframes tw-fly{
  0%   {opacity:1; transform:translate(0,0) rotate(var(--r0)) scale(1)}
  /* it lifts off the felt on the way, which is what makes it read as thrown rather than slid */
  55%  {transform:translate(calc(var(--tx) * .55), calc(var(--ty) * .55 - 16px)) rotate(calc((var(--r0) + var(--r1)) * .5 + 10deg)) scale(calc((1 + var(--s1)) * .53))}
  88%  {opacity:1}
  100% {opacity:0; transform:translate(var(--tx), var(--ty)) rotate(var(--r1)) scale(var(--s1))}
}
@media (prefers-reduced-motion:reduce){ .tw-layer{display:none} }
html[data-reveal="off"] .tw-layer{display:none}

/* ---------- a board card that MOVES (js/reveal.js, v41.22) ----------
   "Zbele 2.0 game where the board cards which are up disapear and cards reappear."
   In Zbele 2.0 a reserve takes over the place of a line card a garbage rank threw away — drawn AT the
   thrown card's position, with its own left empty. Measured on a recorded hand, that is a 237px jump in
   one frame with nothing in between. So it travels: lifted off the felt, carried across, set down. */
.bm-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
.bm-card{
  position:absolute; display:block; border-radius:6px;
  animation-name:bm-slide; animation-timing-function:cubic-bezier(.32,.72,.3,1); animation-fill-mode:both;
}
.bm-card .rv-art{position:absolute; left:0; top:0; transform-origin:0 0}
.bm-card .card{position:absolute; left:0; top:0; margin:0}
@keyframes bm-slide{
  /* no fade at either end: it starts on top of the card it is replacing and ends on top of the card it
     has become, so both handovers are pixel-for-pixel rather than a crossfade */
  0%   {transform:translate(0,0) rotate(var(--r0)) scale(1)}
  45%  {transform:translate(calc(var(--tx) * .5), calc(var(--ty) * .5 - 12px)) rotate(calc((var(--r0) + var(--r1)) * .5)) scale(1.07)}
  100% {transform:translate(var(--tx), var(--ty)) rotate(var(--r1)) scale(1)}
}
/* The real card at the position being moved INTO, held until the copy lands on it. opacity, never
   visibility: an element hidden by visibility takes no pointer events, and a card you cannot yet see
   still has to be a card you can tap (the same rule as .pre-deal and .pre-draw). */
.bm-held{opacity:0}
@media (prefers-reduced-motion:reduce){ .bm-layer{display:none} .bm-held{opacity:1} }
html[data-reveal="off"] .bm-layer{display:none}
html[data-reveal="off"] .bm-held{opacity:1}

/* ---------- the deal (js/reveal.js, v35) ----------
   Card backs skimming from the dealer's seat to each hand. Purely decorative and pointer-inert: the
   real cards and the action bar are live underneath from the first frame, so nobody is ever waiting
   on this to be able to act. */
.dl-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
/* the card inside carries whatever back the player chose */
.dl-card > .card.facedown{position:absolute; inset:0; width:auto; height:auto}
.dl-card{
  position:absolute; width:44px; height:62px; margin:-31px 0 0 -22px;
  border-radius:5px; opacity:0;
  box-shadow:0 6px 14px rgba(0,0,0,.55);
  animation-name:dl-fly; animation-timing-function:cubic-bezier(.22,.7,.3,1); animation-fill-mode:both;
}
@keyframes dl-fly{
  0%  {opacity:0; transform:translate(0,0) rotate(0deg) scale(.72)}
  12% {opacity:1}
  100%{opacity:0; transform:translate(var(--tx), var(--ty)) rotate(var(--tr)) scale(1)}
}
/* a card of a hand that has not been dealt yet. The hand is fully drawn and fully live underneath —
   this only holds each card back until its own card lands on it (v36). Both switches below undo it,
   and reveal.js expires it anyway, so nobody can end up unable to see their own cards.
   Opacity rather than visibility, for the same reason as .rv-hidden above: a card that is not there
   to be tapped is worse than a card you can see, and on a tablet there is no second chance. */
.pre-deal{opacity:0}
@media (prefers-reduced-motion:reduce){ .dl-layer{display:none} .pre-deal{opacity:1} }
html[data-reveal="off"] .dl-layer{display:none}
html[data-reveal="off"] .pre-deal{opacity:1}

/* A slot in your own hand whose card has gone and whose replacement has not arrived (v40). Not a
   back — there is no card there — but not a hole in the fan either: the hand keeps its shape, and
   you can see how many are coming. */
.card.hole-gone{
  background:rgba(10,8,6,.28) !important;
  border:1px dashed rgba(201,164,92,.45) !important;
  box-shadow:none !important;
}
.card.hole-gone > *{display:none !important}

/* ---------- a discard and a draw (js/reveal.js, v39) ----------
   The cards you threw go to the muck, and their replacements come in from the dealer a beat later.
   Its own layer rather than the deal's or the throw pile's, because either of those can be running
   at the same moment and each cancels its own layer wholesale when it finishes.
   `.pre-draw` is opacity, never visibility — see .pre-deal above and v37.1: an element hidden by
   visibility takes no pointer events, so a card you cannot yet see is also a card you cannot tap. */
.dw-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
.dw-layer .tw-card{animation-name:tw-fly; animation-timing-function:cubic-bezier(.3,.75,.35,1)}
.dw-layer .dl-card{animation-name:dl-fly; animation-timing-function:cubic-bezier(.22,.7,.3,1)}
.pre-draw{opacity:0}
@media (prefers-reduced-motion:reduce){ .dw-layer{display:none} .pre-draw{opacity:1} }
html[data-reveal="off"] .dw-layer{display:none}
html[data-reveal="off"] .pre-draw{opacity:1}

/* ---------- a beaten hand pushed to the dealer (js/reveal.js, v42.2) ----------
   "if u do not open a player's cards because he lost, add an animation to throw his cards to the dealer
   when his turn reaches." The same copy and the same flight as a thrown card (.tw-card carries its own
   animation-name), in a layer of its own so that the thrown pile being released at the end of the hand
   and a player mucking a moment later cannot cut each other short. */
.mk-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
@media (prefers-reduced-motion:reduce){ .mk-layer{display:none} }
html[data-reveal="off"] .mk-layer{display:none}

/* ---------- a showdown, hand by hand (v35) ----------
   The opener's hand first, then the rest in turn. The cards are ALREADY in place — this only fades
   and lifts them in on their own delay, so a dropped frame or an interrupted redraw can never leave
   a hand invisible: the end state is the normal one. */
.sd-in{animation:sd-turn .34s ease-out var(--sd, 0ms) both}
@keyframes sd-turn{
  from{opacity:0; transform:translateY(6px) scale(.94)}
  to  {opacity:1; transform:none}
}
@media (prefers-reduced-motion:reduce){ .sd-in{animation:none} }
html[data-reveal="off"] .sd-in{animation:none}

/* ---------- a Cadeau: the gifts go home (js/reveal.js, v41.2) ----------
   Rany: "once all the others have picked, run the animation and populate the players hands."
   Every gift has been lying face down on the felt since its giver confirmed; when the last player
   confirms they all travel at once, because at a table they are all given at the same moment — one
   at a time would say something untrue about the order. Its own layer, like every other animation
   here, so a deal or a draw running at the same instant cancels only its own.
   Decorative and pointer-inert: the hands underneath are already correct from the first frame. */
.gf-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
.gf-card > .card.facedown{position:absolute; inset:0; width:auto; height:auto}
.gf-card{
  position:absolute; width:38px; height:54px; margin:-27px 0 0 -19px;
  border-radius:5px; opacity:0;
  box-shadow:0 6px 14px rgba(0,0,0,.55);
  animation-name:gf-fly; animation-timing-function:cubic-bezier(.25,.72,.3,1); animation-fill-mode:both;
}
@keyframes gf-fly{
  0%  {opacity:1; transform:translate(0,0) rotate(0deg) scale(1)}
  85% {opacity:1}
  100%{opacity:0; transform:translate(var(--tx), var(--ty)) rotate(var(--tr)) scale(.86)}
}
@media (prefers-reduced-motion:reduce){ .gf-layer{display:none} }
html[data-reveal="off"] .gf-layer{display:none}

/* THE POT BEING PUSHED TO WHOEVER WON IT (Rany, v41.19). The chips are the felt's own `.chip` discs,
   so a chip in flight is the same object as a chip in the pot — a flying copy that did not match
   would read as something else entirely arriving. `margin` centres each one on its own point the way
   the pot's stacks are centred, since the flight is worked out from centres.

   The landing is a fade rather than a stop: the winner's stack has already been counted up on his
   plate by the time these arrive, so chips that stayed would be the money shown twice. */
.pf-layer{position:absolute; inset:0; pointer-events:none; z-index:7}
.pf-chip{
  position:absolute; margin:-6px 0 0 -15px; opacity:0;
  animation-name:pf-fly; animation-timing-function:cubic-bezier(.3,.7,.3,1); animation-fill-mode:both;
}
@keyframes pf-fly{
  0%  {opacity:1; transform:translate(0,0) scale(1)}
  78% {opacity:1}
  100%{opacity:0; transform:translate(var(--tx), var(--ty)) scale(.82)}
}
/* a bet or a call landing on its spot: whole, and still there when the spot is drawn under it (v46.5) */
.pf-lands .pf-chip{animation-name:pf-land}
@keyframes pf-land{
  0%  {opacity:1; transform:translate(0,0)}
  100%{opacity:1; transform:translate(var(--tx), var(--ty))}
}
@media (prefers-reduced-motion:reduce){ .pf-layer{display:none} }
html[data-reveal="off"] .pf-layer{display:none}

/* THE DEALER BUTTON, two looks chosen in Options (Rany, v46.5) — <html data-dealer>, set by cards.js.
   On the felt it is `.badges i.btn-d` (23px, table.css); the Options swatch is the same disc
   (`.dealer-look`, with its look named on it). */
.dealer-look{width:23px;height:23px;border-radius:50%;display:grid;place-items:center;font-style:normal;line-height:1}
html:not([data-dealer="brass"]) .badges i.btn-d, .dealer-look[data-look="classic"]{
  background:radial-gradient(circle at 40% 35%, #fffdf6, #efe6cf 60%, #d9cba6);
  color:#15110c;text-shadow:none;
  box-shadow:0 0 0 1.5px #2a1d12, 0 2px 4px rgba(0,0,0,.55);
  font-family:var(--ui, 'Barlow Semi Condensed', sans-serif);font-weight:900;font-size:15px;letter-spacing:0;
}
html[data-dealer="brass"] .badges i.btn-d, .dealer-look[data-look="brass"]{
  background:radial-gradient(circle at 40% 35%, #f7df9a, #d4a84a 60%, #9c7426);
  color:#1d1408;text-shadow:0 1px 0 rgba(255,240,200,.6);
  box-shadow:inset 0 0 0 2px rgba(60,40,10,.55), 0 2px 4px rgba(0,0,0,.6);
  font-family:var(--ui, 'Barlow Semi Condensed', sans-serif);font-weight:900;font-size:15px;letter-spacing:0;
}
