/* Solitaire Library product-local game controls and accessibility. */
.pc:focus-visible, .pile[role="button"]:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.keyboard-help {
  margin: -0.25rem auto 0.75rem;
  max-width: 64ch;
  text-align: center;
  color: var(--ink-faint);
  font-size: 0.85rem;
}
.resume-note {
  margin: 0.1rem auto;
  max-width: 64ch;
  text-align: center;
  color: var(--ink-soft);
  font-size: 0.85rem;
}
.stats-note {
  margin: 0.25rem auto 0.8rem;
  max-width: 70ch;
  text-align: center;
  color: var(--ink-faint);
  font-size: 0.8rem;
}
.deal-replay {
  display: flex;
  gap: 0.55rem;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  margin: 0.7rem 0;
}
.deal-replay label {
  font-weight: 700;
  color: var(--ink-soft);
}
.deal-replay input {
  width: 8ch;
  min-height: var(--tap);
  padding: 0.45rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  font-family: var(--mono);
}
.deal-replay input:focus-visible, .copy-manual-field:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
.deal-error {
  flex-basis: 100%;
  min-height: 1.4em;
  text-align: center;
  color: var(--bad);
  font-size: 0.85rem;
}
.copy-manual-help {
  max-width: 64ch;
  margin: 1rem auto 0.35rem;
  color: var(--bad);
  text-align: center;
  font-weight: 700;
}
.copy-manual-field {
  display: block;
  width: min(64ch, calc(100% - 2.5rem));
  min-height: 5.5rem;
  margin: 0 auto 1rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--bad);
  border-radius: 8px;
  background: var(--card);
  color: var(--ink);
  font: 1rem/1.4 var(--mono);
}

/* ============================================================
   Card-table redesign (Spider, Klondike) — a reading-room card
   table, not a casino: baize, walnut rail, brass UI accents.
   Single light look only; the rest of this site has no dark
   theme to match, so this does not add one on its own.
   ============================================================ */
:root {
  --table-baize: #1E4A39;
  --table-walnut: #3B2A1E;
  --table-stock: #FAF8F2;
  --table-ink: #1B1D22;
  --table-red: #B3261E;
  --table-brass: #C9A54C;
  --table-card-edge: rgba(27, 29, 34, 0.28);
}
.vh { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* The shared kit's bare `main` selector (base.css) pads and caps every page's
   <main> at 1060px for prose/article content — wrapping the table-room in
   one <main> (the a11y fix below) means this page's <main> must shed that
   padding/cap itself, or the rail/baize go edge-to-edge no more than 300px
   out of 320. #below re-applies the same 1.25rem/1060px padding for its own
   content, so nothing below the table changes. */
.table-main { max-width: none; margin: 0; padding: 0; }
.table-room { display: flex; flex-direction: column; height: 100dvh; min-height: 480px; background: var(--table-baize); }

.table-rail { flex: none; background: var(--table-walnut); padding-top: env(safe-area-inset-top, 0px); border-bottom: 1px solid rgba(201, 165, 76, 0.28); position: relative; z-index: 30; }
.table-rail-in { height: 48px; display: flex; align-items: center; gap: 1px; padding: 0 6px; max-width: 1200px; margin: 0 auto; }
.table-title { margin: 0; color: var(--table-stock); font-size: 1.05rem; font-weight: 700; letter-spacing: -0.01em; white-space: nowrap; flex: none; }
.table-tools { margin-left: auto; display: flex; align-items: stretch; height: 48px; }
/* Icon-only through every phone width the gate checks (320/375/390): seven
   controls plus the H1 cannot ALSO carry visible text labels in a 48 px
   rail at those widths without overflowing. aria-label still names every
   control; labels become visible text once there is real room for them.
   Below 560 px each tool asks for the 44 px tap-target floor (width)
   and may shrink toward 32 px only when the row would otherwise overflow:
   measured in Chrome, every tool on both tables gets the full 44 px at the
   390 px phone-first baseline; Klondike (the longer H1) leaves about 40 px
   at 360 px and 34 px at 320 px,
   and the rail never scrolls sideways (the smoke gate's header check pins
   320/375/390). */
.tool {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  flex: 0 1 auto; width: 44px; min-width: 32px; min-height: 44px; padding: 2px; color: var(--table-stock);
  background: transparent; border: 0; border-radius: 4px; font: inherit; font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.01em; cursor: pointer; white-space: nowrap;
}
.tool svg { width: 18px; height: 18px; stroke: var(--table-brass); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.tool .lbl { display: none; }
.tool:hover { background: rgba(201, 165, 76, 0.12); }
.tool:focus-visible { outline: 2px solid var(--table-brass); outline-offset: 1px; }
.tool[aria-expanded="true"] { background: rgba(201, 165, 76, 0.2); }
.tool:disabled { opacity: 0.4; cursor: default; background: transparent; }
@media (max-width: 559.98px) {
  .table-rail-in { gap: 0; padding: 0 4px; }
  .table-title { font-size: 0.95rem; }
  .table-tools { min-width: 0; }
}
@media (min-width: 560px) {
  .table-rail-in { gap: 2px; padding: 0 10px; }
  .table-title { font-size: 1.25rem; }
  .tool { flex: none; width: auto; min-width: 46px; padding: 2px 6px; font-size: 0.66rem; gap: 1px; }
  .tool svg { width: 20px; height: 20px; }
  .tool .lbl { display: block; }
}

.table-drawer, .table-settings {
  position: absolute; left: 0; right: 0; top: calc(48px + env(safe-area-inset-top, 0px)); z-index: 45;
  background: var(--table-walnut); box-shadow: 0 16px 28px rgba(0, 0, 0, 0.32); padding: 12px 14px 16px;
}
.table-settings { left: auto; right: 8px; width: min(300px, calc(100vw - 24px)); border-radius: 0 0 6px 6px; background: var(--table-stock); color: var(--table-ink); }
.table-settings h2 { margin: 0 0 8px; font: 700 0.72rem/1 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; }
.table-drawer-list { list-style: none; display: flex; flex-wrap: wrap; gap: 6px; max-width: 1200px; margin: 0 auto; }
.table-drawer-list a {
  display: block; padding: 0.55rem 0.8rem; min-height: 44px; display: flex; align-items: center;
  color: var(--table-stock); background: rgba(250, 248, 242, 0.08); border: 1px solid rgba(201, 165, 76, 0.3);
  border-radius: 4px; font-weight: 700; font-size: 0.9rem;
}
.table-drawer-list a:hover { background: rgba(201, 165, 76, 0.18); text-decoration: none; }
.table-drawer-list a[aria-current="page"] { background: var(--table-brass); color: var(--table-walnut); }
.table-settings .game-bar { justify-content: flex-start; margin: 0.3rem 0 0.8rem; }
.table-settings .fine { margin-top: 0.6rem; }

.table-felt {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; position: relative;
  -webkit-overflow-scrolling: touch;
}
.table-felt .sol-wrap { max-width: 1200px; margin: 0 auto; padding: 10px 8px 22px; }
.table-felt .sol-top, .table-felt .sol-board { gap: 2px; }
.table-felt .pile.outline { border-color: rgba(250, 248, 242, 0.3); }
.table-felt .deal-id { color: var(--table-stock); opacity: 0.85; }
/* The redesigned table draws the whole card (face AND back) as the SVG
   itself (Cards.svgEl), so the family kit's .pc/.pc.down div styling
   (the blue-striped CSS back, the white div background, the thin generic
   border) is reset here — otherwise it shows through at the SVG's edges,
   since the svg does not stretch to literally 100% of a sub-pixel box.
   padding is reset too: the family kit's .pc carries a text padding for its
   old DOM-rank rendering, and left in place here it shrinks the SVG below
   the full --cardw/--cardh box (and below today's card size) without ever
   showing up as a layout overflow, since width/height still equal the
   padded box. */
.table-felt .pc { background: none; border: 0; padding: 0; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.28); }
.table-felt .pc svg { display: block; width: 100%; height: 100%; }
.table-felt .pc.down { background: none; border-color: transparent; }
/* A finger drag needs touch-action: none on the card itself, or the first
   move event is still interpreted as a page pan/scroll and the browser
   fires pointercancel on the drag before it ever starts. */
.table-felt .pc[role="button"] { touch-action: none; }
/* pointer-events: none is load-bearing, not cosmetic — the dragged card
   follows the pointer via transform, so without this the card itself is
   what document.elementsFromPoint() hits at the drop point, and every
   drop reads back the card's OWN origin pile instead of whatever pile is
   actually under the cursor. */
.table-felt .pc.dragging { z-index: 90 !important; box-shadow: 0 10px 22px rgba(0, 0, 0, 0.38); cursor: grabbing; pointer-events: none; }
/* Card face/back fill and font, as CSS classes rather than var() inside an
   SVG presentation attribute string (site/kit/cards.js svgFace/svgBack) —
   custom-property resolution inside an SVG attribute is unverified on older
   WebKit/iOS Safari, where a normal CSS rule is unambiguous. */
.pc-card-bg { fill: var(--table-stock); stroke: var(--table-card-edge); }
.pc-ink { fill: var(--table-ink); font-family: var(--sans); }
.pc-red { fill: var(--table-red); font-family: var(--sans); }
.pc-back-panel { fill: var(--table-walnut); stroke: var(--table-card-edge); }
.pc-back-frame { stroke: var(--table-brass); }
.pc-back-frame-outer { stroke-opacity: 0.55; }
.pc-back-frame-inner { stroke-opacity: 0.3; }
.pc-back-mark { stroke: var(--table-brass); stroke-opacity: 0.85; }
.table-felt .pile.drop-target { outline: 2px solid var(--table-brass); outline-offset: 2px; background: rgba(201, 165, 76, 0.1); border-radius: 6px; }
/* The family kit's focus ring is the blue accent, which is close to
   unreadable against this table's dark green baize (about 1.4:1). Brass
   already carries the required 3:1 non-text contrast on baize everywhere
   else in this design, so the redesigned table's own cards and piles use
   it for focus too. */
.table-felt .pc:focus-visible, .table-felt .pile[role="button"]:focus-visible {
  outline: 3px solid var(--table-brass);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .table-felt .pc { transition: none !important; }
}
/* 7 columns (Klondike) and 10 columns (Spider) size cards to exactly fill
   the available WIDTH, capped at 92/76 px past 1200 px where the table
   itself caps — not a guess, so there is never a horizontal scrollbar to
   fix later. A third min() term caps by HEIGHT too: width-only sizing made
   a landscape screen's cards taller than the felt (measured: Klondike's
   column 7 was fully clipped below the felt at 1200x800, at 1194x834 — an
   iPad Pro 11 landscape — at 1024x768, and at 844x390 phone-landscape).
   --felt-chrome is everything besides cards that eats into 100dvh at a
   fixed pixel cost regardless of --cardw: the walnut rail (49px), the
   message row (45px, empty most of the time but still reserved), the
   status strip (32px), the felt's own wrap padding (10+22), and the top
   row's margin-bottom (16); Spider has no message band and overrides this
   below — plus the safe-area insets the rail/status
   already pad themselves by, so this does not double-count them. What's
   left is divided by how many card-heights (1.42 * cardw = --cardh) the
   TALLEST stack needs: one card for the top row, plus the longest opening
   tableau column — cardh for its one face-up card, plus a 0.26*cardw step
   per face-down card beneath it (cards.js's FAN_DOWN). Klondike's longest
   opening column is 7 cards, 6 face-down: 1.42 + (1.42 + 6*0.26) = 4.4.
   Spider's is 6 cards, 5 face-down: 1.42 + (1.42 + 5*0.26) = 4.14.
   Tightening the fan as columns grow (so a LATE-game column doesn't hit the
   same ceiling) can follow in a later PR. */
.table-felt {
  --felt-chrome: calc(49px + 45px + 32px + 32px + 16px + env(safe-area-inset-top, 0px) + env(safe-area-inset-bottom, 0px));
}
.table-felt[data-game="klondike"] { --cardw: min((100vw - 48px) / 7, 92px, calc((100dvh - var(--felt-chrome)) / 4.4)); }
.table-felt[data-game="spider"] { --cardw: var(--sp-cardw); }
/* --cardh is declared on :root (base.css) as calc(var(--cardw) * 1.42), so
   it is resolved ONCE there against :root's own --cardw and simply inherits
   down unchanged — the --cardw overrides above, two levels down on
   .table-felt, never cause --cardh to be recalculated. Redeclaring --cardh
   here, on the same element that overrides --cardw, resolves var(--cardw)
   against THIS element's winning value instead, so every .pc beneath it
   inherits the correct height for the felt it's actually in. Spider's board
   also carries the shared kit's .board-10, whose own --cardw (base.css,
   for the OLD fixed-header layout) sits on a child of .table-felt and so
   wins there by plain inheritance proximity, not specificity — overridden
   back to the felt's own formula so the fan/board math below uses the same
   width the cards are actually drawn at. */
.table-felt[data-game="klondike"], .table-felt[data-game="spider"] {
  --cardh: calc(var(--cardw) * 1.42);
}
.table-felt .board-10 { --cardw: var(--sp-cardw); }

/* Spider, sized to the screen (plan 12.11: about 36 px a card at 390 px and
   about 84 px at 1280x800, with the table starting right under the rail).
   - No message band: #game-msg sits beside the stock in .table-head, so
     Spider's fixed chrome is the rail (49), status strip (32), wrap padding
     (10+22) and the head row's 16 px margin, with no 45 px reserve.
   - Width comes from the felt's own inline size (a container), not 100vw:
     100vw counts a desktop scrollbar, which is why the old formula kept a
     54 px margin and left about 36 px of baize unused on a phone. Ten
     columns, nine 2 px gaps and 6 px side padding need 10w + 30; the extra
     pixel absorbs sub-pixel rounding. A browser without container units
     keeps the old 100vw formula, with the new cap.
   - The cap rises from 76 px to 84 px; the height term still wins on a
     short screen, and fitColumns() still shrinks for a long column. */
.table-felt[data-game="spider"] {
  --felt-chrome: calc(49px + 32px + 32px + 16px + env(safe-area-inset-top, 0px) + env(safe-area-inset-bottom, 0px));
  --sp-cardw: min((100vw - 54px) / 10, 84px, calc((100dvh - var(--felt-chrome)) / 4.14));
}
@supports (width: 1cqw) {
  .table-felt[data-game="spider"] {
    container-type: inline-size;
    --sp-cardw: min((100cqw - 31px) / 10, 84px, calc((100dvh - var(--felt-chrome)) / 4.14));
  }
}
.table-felt[data-game="spider"] .sol-wrap { padding-left: 6px; padding-right: 6px; }
/* The stock row lines up with column 1, like a real layout, instead of
   floating in the middle: the head is exactly as wide as the ten columns.
   The stock spans both rows; the stock count sits top right and the game
   message under it. The row is one card tall whatever the message says,
   so a message never moves the board. */
.table-felt[data-game="spider"] .table-head {
  display: grid; grid-template-columns: var(--cardw) minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
  column-gap: 10px; width: calc(10 * var(--cardw) + 18px); max-width: 100%; height: var(--cardh); margin: 0 auto 16px;
}
.table-felt[data-game="spider"] .table-head .sol-top { display: contents; }
.table-felt[data-game="spider"] .table-head #sp-stock { grid-column: 1; grid-row: 1 / span 2; }
.table-felt[data-game="spider"] .table-head .deal-id { grid-column: 2; grid-row: 1; font-size: 0.8rem; line-height: 1.3; }
.table-felt[data-game="spider"] .table-head .game-msg {
  grid-column: 2; grid-row: 2; margin: 0; min-height: 0; text-align: left; font-size: 0.8rem; line-height: 1.25;
  pointer-events: none;
}
.table-felt[data-game="spider"] .sol-board { justify-content: center; min-height: 0; }
/* The rail H1 carries the suit count ("Spider · 2 suits"). Below 560 px the
   count drops under the name in small type, so the H1 is no wider than the
   bare name and all seven tools keep their 44 px; the separator stays in the
   accessible name. */
.table-title .tt-sub { font-weight: 400; color: var(--table-stock); }
@media (max-width: 559.98px) {
  .table-title .tt-sep { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .table-title .tt-sub { display: block; font-size: 0.66rem; line-height: 1; letter-spacing: 0.02em; }
  .table-title { line-height: 1.1; }
}

/* #game-msg ("Run cleared", the win message, deal/resume notices) now sits on
   the dark baize instead of the family kit's light card background, but it
   kept the kit's dark --ink color — about 1.76:1 against --table-baize,
   well under the 4.5:1 text floor. --table-stock is the light color already
   used for every other label on the baize/rail/status strip. */
.table-room .game-msg { color: var(--table-stock); }
.table-status { flex: none; background: var(--table-walnut); padding-bottom: env(safe-area-inset-bottom, 0px); }
.table-status-in {
  height: 32px; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 0 10px; max-width: 1200px; margin: 0 auto; color: var(--table-stock); font-size: 0.78rem; white-space: nowrap;
}
.table-status-in .status-call { opacity: 0.92; overflow: hidden; text-overflow: ellipsis; }

#below { max-width: 1060px; margin: 0 auto; padding: 1.1rem 1.25rem 0; }
#below .deal-card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 1rem 1.1rem; margin-bottom: 0.4rem; }
#below .deal-replay, #below .sol-controls { margin-top: 0; }
