* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* A saloon table rather than a card room: this is the one game in the
     catalogue where the deal itself is the gamble, and the felt says so. */
  background: linear-gradient(180deg, #1e1b4b 0%, #312e81 55%, #1e1b4b 100%);
  color: #eef2ff; user-select: none; -webkit-user-select: none;
  touch-action: none; overflow: hidden;
}
#app { height: 100%; display: flex; flex-direction: column; max-width: min(100%, 860px); margin: 0 auto; }

#hdr { display: flex; align-items: center; gap: 8px; padding: 8px 12px; padding-top: max(8px, env(safe-area-inset-top)); }
#hdr .pill { background: rgba(0,0,0,0.22); border: 2px solid rgba(255,255,255,0.16); border-radius: 999px; padding: 6px 11px; font-weight: 800; font-size: 15px; min-width: 44px; min-height: 44px; display: flex; align-items: center; cursor: pointer; justify-content: center; color: inherit; font-family: inherit; }
#hdrHome { border: none; background: #fbbf24; color: #1e1b4b; font-size: 18px; }
#hdrStatus { flex: 1; min-width: 0; text-align: center; font-weight: 800; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #c7d2fe; }

.scr { flex: 1; display: none; flex-direction: column; overflow-y: auto; padding: 6px 14px 18px; }
/* The table gets the full width: side padding comes straight off the card size. */
#scr-play { padding: 6px 4px 6px; }
.scr.on { display: flex; }

#scr-menu { align-items: center; text-align: center; gap: 12px; justify-content: center; }
#scr-menu h1 { font-size: clamp(32px, 9vw, 44px); color: #ffffff; }
#scr-menu .sub { color: #c7d2fe; font-weight: 600; margin-bottom: 4px; }
#tierRow { display: flex; gap: 8px; width: 100%; }
.tierCard { flex: 1; background: rgba(0,0,0,0.2); border: 3px solid rgba(255,255,255,0.14); border-radius: 16px; padding: 10px 4px; font-weight: 800; font-size: 14px; line-height: 1.35; cursor: pointer; color: #eef2ff; font-family: inherit; }
.tierCard small { display: block; color: #c7d2fe; font-weight: 700; font-size: 11.5px; }
.tierCard .te { font-size: 26px; display: block; }
.tierCard.sel { border-color: #fbbf24; background: rgba(251,191,36,0.14); }

/* Settings, behind a disclosure — the same shape as every other product's. */
#custom { width: 100%; margin: 2px 0; text-align: left; background: rgba(0,0,0,0.2); border: 3px solid rgba(255,255,255,0.13); border-radius: 16px; padding: 6px 12px 10px; }
#custom > summary { list-style: none; cursor: pointer; font-weight: 800; font-size: 16px; padding: 10px 2px; min-height: 44px; display: flex; align-items: center; gap: 8px; }
#custom > summary::-webkit-details-marker { display: none; }
/* The changed-rule badge on the closed panel (#256), same shape as the rummy
   pair's: pushed right, and it ellipsises rather than widening the summary. */
#custom > summary .badge { margin-left: auto; font: 400 12px/1 inherit; opacity: .72; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#custom > summary::before { content: "▸"; transition: transform .12s; }
#custom[open] > summary::before { transform: rotate(90deg); }
.field { display: flex; align-items: center; gap: 10px; margin: 7px 0; }
.field .lbl { flex: 0 0 104px; font-size: 13.5px; font-weight: 800; color: #c7d2fe; }
.seg { flex: 1; display: flex; gap: 6px; }
.seg button { flex: 1; min-height: 44px; border-radius: 11px; border: 2px solid rgba(255,255,255,0.16); background: rgba(0,0,0,0.24); color: #eef2ff; font-family: inherit; font-weight: 800; font-size: 13.5px; cursor: pointer; padding: 4px 2px; }
.seg button.on { background: #fbbf24; border-color: #4338ca; color: #1e1b4b; min-height: 44px;}
.blurb { margin: 6px 0 2px; font-size: 12.5px; line-height: 1.45; color: #c7d2fe; }

button.big { width: 100%; border-radius: 16px; min-height: 56px; font-size: 19px; font-weight: 800; background: rgba(0,0,0,0.2); color: #eef2ff; border: 3px solid rgba(255,255,255,0.14); cursor: pointer; font-family: inherit; }
button.big:active { transform: translateY(2px); }
button.big.primary { background: #fbbf24; border-color: #4338ca; color: #1e1b4b; min-height: 44px;}

/* --- play ----------------------------------------------------------------- */
/* THE SCOREBOARD ROW (R-C19). Fixed height, and nothing in it changes width by
   more than a digit, so a message arriving cannot shove the numbers sideways. */
/* WRAPS (R-G26.9). The row carries three BUTTONS and two chips, and the sort
   button's label is the mode name, so its width is decided at runtime: measured
   at 320px the row was 390px of content and `#titleChip` ("you are—" plus the
   rank you are playing for) sat entirely off the screen, still 19px out at 375.
   Wrapping costs a line only when the row cannot hold; `min-height` stays, so
   the single-line case is unchanged and the numbers still do not move (R-C19). */
#playTop { display: flex; flex-wrap: wrap; font-size: 13px; gap: 5px; min-height: 30px;
           align-items: stretch; justify-content: flex-start; padding: 2px 2px 3px;
           font-weight: 800; }
#playTop .gap { flex: 1 1 auto; min-width: 0; }
.chip { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
        padding: 0 8px; border-radius: 999px; background: rgba(0,0,0,0.26);
        border: 2px solid rgba(255,255,255,0.14); line-height: 1; }
.chip i { font-style: normal; font-size: 10px; font-weight: 800; opacity: 0.72; letter-spacing: 0.02em; }
.chip b { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; }
/* The trump chip lights up the moment trump exists, and carries the suit's own
   colour: a red suit on a warm felt is not readable as "red" without it. */
.chip#trumpChip.set { border-color: #fbbf24; background: rgba(251,191,36,0.18); }
.chip#trumpChip.red b { color: #e0e7ff; }
.chip#bidChip.set { border-color: #fbbf24; background: rgba(251,191,36,0.16); }
.chip.you { border-color: rgba(253,230,138,0.55); color: #fde68a; }

#msgLine { flex: none; min-height: 20px; display: flex; align-items: center; justify-content: center; padding: 0 4px 3px; }
#msgOut { font-weight: 800; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

#stageWrap { flex: 1; position: relative; min-height: 0; }
canvas#stage { display: block; touch-action: none; }

/* #303b. AUTO MARGINS, NOT align-items: center. Both centre a card that fits.
   They differ only when it does not, and that is the case that matters: with
   `align-items: center` a taller-than-container item is centred anyway, so half
   the overflow sits ABOVE scrollTop 0, where no gesture reaches it (#71, and
   measured again here at -767px in landscape). An auto margin resolves to 0
   when free space is negative, so the card starts at the top and every pixel of
   it is scrollable. */
.ovl { position: absolute; inset: 0; display: none; align-items: flex-start; justify-content: center; background: rgba(69,26,3,0.55); border-radius: 12px; padding: 16px; overflow-y: auto; }
.ovl.on { display: flex; }
/* The overlay REPORTS ON THE TABLE, so it must not hide the table: the scrim is
   a dim and the words sit on a panel of their own. */
/* #303b. THE CARD STOPS AT 75% AND ITS BODY SCROLLS.
   An overlay that reports a LIST grows with the list. After thirty hands the
   history card measured 4693px tall inside a play area a fraction of that, and
   because .ovl centres its child the overflow went UPWARDS: 2330px of it sat
   above the scroll origin, where no gesture can reach it. The title was off the
   top, the "Got it" button was off the bottom, and the only thing on screen was
   a wall of rows — #71 all over again, in a different product.
   Capping the CARD and scrolling its BODY keeps the title and the button
   pinned where the player expects them, and it fixes every overlay at once
   rather than only the one that grew.
   `overflow-y: auto` and NOT `hidden`: a cap is a promise about size, not a
   licence to delete. The first attempt clipped, and in a 198px landscape play
   area — where the title, both buttons and one line of body already need 218px
   — it cut the "Got it" button in half and swallowed "Menu" entirely. `auto`
   costs nothing when the card fits (which is every case with room) and keeps
   the way out reachable when it does not. */
.ovlCard { text-align: center; width: 100%; max-width: 340px; display: flex; flex-direction: column; gap: 10px;
           margin: auto; max-height: 75%; overflow-y: auto;
           background: rgba(60,20,4,0.96); border: 2px solid rgba(255,255,255,0.16);
           border-radius: 18px; padding: 16px 14px; box-shadow: 0 10px 30px rgba(0,0,0,0.45); }
#ovlTitle { font-size: 26px; font-weight: 800; color: #ffffff; flex: 0 0 auto; }
/* min-height IS the shrink permission. A flex item defaults to min-height:auto,
   which refuses to shrink below its content — the cap would be ignored and the
   card would grow anyway. The explicit value both floors an empty body and
   allows the overflow. */
#ovlSub { color: #c7d2fe; font-weight: 700; min-height: 22px; line-height: 1.45;
          display: flex; flex-direction: column; gap: 5px;
          flex: 0 1 auto; overflow-y: auto; overscroll-behavior: contain; }
/* The one control on the card never shrinks. It is the way out of the overlay
   and it carries the 44px tap floor (R-G26b); squeezing it to make room for
   more rows would trade the exit for the list. */
#ovlBtn { flex: 0 0 auto; }
/* #303b. A CAP NEEDS SOMETHING TO CAP. In a short landscape window the play
   area is 198px and the card's fixed furniture alone is 218px, so 75% of it
   buys the list a 22px window — a nested scroller too small to flick and too
   small to read. Below that height there is no space to divide: drop the cap
   and let the whole card scroll inside the scrim as one thing, which the auto
   margin above makes safe. */
@media (orientation: landscape) and (max-height: 500px) {
  .ovlCard { max-height: none; overflow-y: visible; margin: auto;}
  #ovlSub { overflow-y: visible; }
}
/* #303b. THE TALLY IS PINNED. It is the answer to "how am I doing", so it must
   not be the thing that scrolls away first. Sticky inside the scroller keeps it
   over the rows it counts, and the opaque background stops the list showing
   through as it passes underneath. */
.sdTally { position: sticky; top: 0; z-index: 1; margin: 0 0 4px;
           background: rgba(60,20,4,0.98); padding: 2px 0 5px;
           font-size: 11px; line-height: 1.3; color: #cbd5e1; font-weight: 700; }

/* THE RESULT ROWS ("a result must carry its evidence"). One row per player:
   who, what happened, what it was worth. Same class names as the other card
   games, because it is the same idea and a second vocabulary would drift. */
.sdRow { display: flex; align-items: center; gap: 7px; padding: 5px 7px; border-radius: 10px;
         background: rgba(255,255,255,0.05); border: 2px solid transparent; text-align: left; }
.sdRow.you { background: rgba(255,255,255,0.10); }
.sdRow.win { border-color: #4ade80; background: rgba(74,222,128,0.14); }
.sdWho { flex: 0 0 62px; font-size: 13px; font-weight: 800; }
.sdHand { flex: 1 1 auto; font-size: 12px; font-weight: 700; line-height: 1.25; color: #c7d2fe; }
/* #254. The line under a row of cards that says WHY it changed hands. Indented
   past .sdWho's 62px column so it reads as belonging to the row above it. */
.sdNote { margin: 1px 0 6px 69px; font-size: 11px; line-height: 1.3; color: #94a3b8; }

/* #303. One row per finished hand: the hand number, YOUR title, and who else
   placed. Two columns rather than a table, so the eye can run straight down
   the middle one — which is the column the player opened this for. The 62px
   gutter lines the rows up with .sdNote's existing 69px indent, so the tally
   at the bottom sits under the titles rather than under the numbers. */
.sdHandRow { display: grid; grid-template-columns: 62px 1fr; gap: 2px 8px;
             align-items: baseline; padding: 6px 0;
             border-bottom: 1px solid rgba(255,255,255,0.07); }
.sdHandRow b { font-size: 12px; color: #94a3b8; font-weight: 800; }
.sdMine { font-size: 15px; font-weight: 800; color: #fde68a; }
.sdHandRow small { grid-column: 2; font-size: 11px; color: #94a3b8; }

/* #302. The exchange is the most consequential moment in the game and it was
   drawn at .sdHand's 12px — R-G26's legibility FLOOR, not a size you choose
   for the thing that decides the hand. A Joker is rendered as the emoji 🃏,
   and an emoji at 12px is a smudge: the reported symptom was "You gave up"
   followed by something the player could not identify.
   20px is above the floor by a comfortable margin and still fits four cards
   across the narrowest supported overlay. */
.sdHand.sdSwap { font-size: 20px; line-height: 1.35; letter-spacing: 0.06em; }

/* THERE IS NO .sdSwing HERE, AND THAT IS THE GAME. R26: President keeps no
   score, so a result row has a place and a title and nothing to put in a
   third, numeric column. Pitch's stylesheet — which this one started as — had
   one, along with a bid row and a tricks record. `just check-stability` found
   the ids; the classes went with them by hand. */

/* --- help ----------------------------------------------------------------- */
#scr-how .card { background: rgba(0,0,0,0.2); border: 3px solid rgba(255,255,255,0.13); border-radius: 16px; padding: 14px; margin-bottom: 10px; font-size: 15px; line-height: 1.55; font-weight: 600; }
#scr-how h2 { margin-bottom: 8px; color: #ffffff; }
#scr-how ul { margin: 6px 0 0 18px; }
#scr-how li { margin: 3px 0; }

/* The legend (R-L1). One column of swatches at a fixed width, so the eye can
   run the column instead of the prose. */
.legend { display: flex; flex-direction: column; gap: 7px; }
.lgRow { display: flex; align-items: center; gap: 10px; }
.lgArt { flex: none; width: 46px; display: flex; align-items: center; justify-content: center; }
.lgTxt { display: flex; flex-direction: column; min-width: 0; }
.lgTxt b { font-size: 14px; font-weight: 800; }
.lgTxt small { font-size: 12.5px; color: #c7d2fe; line-height: 1.3; font-weight: 600; }
.lgPip { font-size: 22px; color: #0f172a; background: #fff; border-radius: 4px; padding: 1px 6px; }
.lgPip.red { color: #dc2626; }
.lgPip.joker { color: #7c3aed; }   /* the star render.js paints, same hex */
.lgD { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
       border-radius: 999px; background: #fbbf24; color: #3b1d00; font-weight: 800; font-size: 14px; }
/* The two seat states, in the SAME two colours render.js paints them on the
   felt (#94a3b8 passed, #4ade80 finished). Two names for one colour is how a
   legend stops describing the game it is a legend for. */
.lgD.passed { background: #94a3b8; }
.lgD.out    { background: #4ade80; }

#toast { position: fixed; left: 50%; bottom: 84px; transform: translateX(-50%); background: #ffffff; color: #1e1b4b; font-weight: 800; padding: 9px 16px; border-radius: 999px; opacity: 0; transition: opacity 0.25s; pointer-events: none; z-index: 40; max-width: calc(100vw - 32px); text-align: center; }
#toast.on { opacity: 1; }

/* docs/offline-pwa.md R-C15 — the PAGE must never scroll. */
html, body { overflow: hidden; overscroll-behavior: none; }
.scr { overscroll-behavior: contain; }

/* A TURNED PHONE HAS NO SPARE ROWS (R-T6). Every line of chrome here is a line
   the felt does not get, and at 375px of window height the felt is what the
   game IS. The text shrinks and the reserved rows tighten; the 44px touch floor
   on the action buttons does not move, because that is the one thing that must
   never give way — see the #actRow block below, which owns it. */
@media (orientation: landscape) and (max-height: 500px) {
  #hdr { padding: 4px 10px; padding-top: max(4px, env(safe-area-inset-top)); }
  #playTop { padding: 0 4px 2px; font-size: 12px; min-height: 0; }
  #msgLine { min-height: 15px; padding: 0 4px 1px; }
  #msgOut { font-size: 11.5px; }
}
/* #228: the landscape footer float moved into version.js, which owns the
   footer's layout for all 54 products. Nothing product-specific was left. */

/* ---- President's own two controls ---------------------------------------
   The action row and the hand-sort button, taken from the rummy pair because
   this is a shedding game with a fan you choose from, not a trick game where
   one tap is the whole turn. Same geometry, so the felt contract's reserved
   bands (R-T1) mean the same thing here as there. */
#actRow { flex: none; display: flex; flex-wrap: nowrap; gap: 6px; justify-content: center;
          align-items: stretch; padding: 6px 6px max(4px, env(safe-area-inset-bottom));
          min-height: 60px; }
#actRow button { flex: 1 1 0; min-height: 48px; min-width: 0; padding: 0 8px;
                 font: 800 15px/1.15 system-ui, sans-serif; border-radius: 12px;
                 border: 2px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.10);
                 color: #eef2ff; cursor: pointer; }
#actRow button.primary { background: #4338ca; border-color: #6366f1; color: #ffffff; }
#actRow button.off { opacity: 0.45; }
#actRow button.cancel { flex: 0 0 auto; }
#actRow button:active { transform: translateY(2px); }
@keyframes presPulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.04); } }
#actRow button.pulse { animation: presPulse 1s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { #actRow button.pulse { animation: none; } }

.sortBtn { flex: none; min-height: 44px; min-width: 44px; padding: 0 10px;
           font: 800 14px/1 system-ui, sans-serif; border-radius: 10px;
           border: 2px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.10);
           color: #eef2ff; cursor: pointer; }
.sortBtn:active { transform: translateY(2px); }
/* #266. AUTO ON HAS TO BE UNMISTAKABLE. The playbook's rule is that automation
   the player cannot see cannot be trusted, so the ON state is not a subtle
   tint: filled amber, dark text, and it stays that way for as long as the
   game is playing itself. */
#btnPlayForMe[aria-pressed="true"] { background: #f59e0b; border-color: #fbbf24;
                                color: #1c1917; }

@media (orientation: landscape) and (max-height: 500px) {
  /* THE RIGHT-HAND 56px BELONGS TO THE VERSION CHIP. #228 floats it into the
     bottom-right corner in all 54 products, and President's action row runs the
     full width — so 👉 Pass and the chip were drawn on top of each other. The
     ROW yields, not the chip: the chip's position is one shared rule, and a
     product-specific exception to a shared rule is how a sweep stops being one.
     It has to be in the `padding` SHORTHAND here rather than a padding-right of
     its own further up, because this block is later in the file and the
     shorthand resets all four sides — which is exactly how the first attempt
     at this line did nothing at all. */
  #actRow { min-height: 52px; padding: 4px 56px max(2px, env(safe-area-inset-bottom)) 4px; }
  #actRow button { min-height: 44px; font-size: 14px; }
}
