/* 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; }
/* The rail is one 48 px row whenever the H1 and the tools fit side by side,
   at EVERY width. When they do not (a narrow phone, or larger text), the
   tools drop to their own row under the H1 instead of pushing the page
   sideways. On a screen too narrow for seven 44 px tools in one row (under
   312 px of content, such as a 280 px folding phone or a 320 px desktop
   window with a 15 px scrollbar) they wrap again inside their own row. The tools never shrink below their size; the wrap is what gives way.
   Smoke 26 sweeps every width from 280 to 1280 px to hold this. */
.table-rail-in { min-height: 48px; display: flex; flex-wrap: wrap; 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; flex: none; flex-wrap: wrap; justify-content: flex-end; align-items: stretch; min-height: 48px; max-width: 100%; }
/* Icon-only through every phone width the gate checks (320/360/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 every tool keeps the full 44 px tap-target floor at every
   width. They used to shrink toward 32 px when the row ran out of room
   (measured: 42.5 px at 360 and 36.8 px at 320 on Spider, 39.8 and 34.1 on
   Klondike). Now the row wraps instead (above). It wraps only when they
   truly do not fit (Spider still fits one row at 375 and up, Klondike at
   390 and up), so the phone-first 390 layout keeps its single 48 px rail.
   The drawer and Settings sheet open under the rail's real bottom edge
   (cards.js bindDisclosures), whichever height it has. */
.tool {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  flex: none; width: 44px; min-width: 44px; 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; }
}
@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 and Klondike no longer have that message
   band and each override 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; }
/* Klondike, sized to the screen (plan 12.11: about 52 px a card at 390 px,
   with the table starting right under the rail).
   - The message no longer has its own 45 px band above the felt. It sits in
     a two-line band between the stock row and the tableau, in place of
     the old 16 px row gap, so the stock row starts right under the rail
     and, at default text settings, no message moves the board.
   - Fixed chrome: rail (49), status strip (32), wrap padding (10+22) and the
     32 px message band. The 4.4 card-height divisor is unchanged.
   - Width comes from the felt's own inline size, as on Spider: seven
     columns, six 2 px gaps and 6 px side padding need 7w + 24; the extra
     pixel absorbs sub-pixel rounding. The 92 px cap is unchanged, so the
     desktop cards stay at 92 px. A browser without container units keeps
     the old 100vw formula. */
.table-felt[data-game="klondike"] {
  --felt-chrome: calc(49px + 32px + 32px + 32px + env(safe-area-inset-top, 0px) + env(safe-area-inset-bottom, 0px));
}
@supports (width: 1cqw) {
  .table-felt[data-game="klondike"] {
    container-type: inline-size;
    --cardw: min((100cqw - 25px) / 7, 92px, calc((100dvh - var(--felt-chrome)) / 4.4));
  }
}
.table-felt[data-game="klondike"] .sol-wrap { padding-left: 6px; padding-right: 6px; }
.table-felt[data-game="klondike"] .sol-top { margin-bottom: 0; flex-wrap: nowrap; }
/* The band is AT LEAST 32 px (two lines of 0.8rem at 1.25), not exactly 32 px.
   At default text settings every message fits two lines at 320 px and up,
   so the band stays 32 px and the board never moves. When the reader has
   enlarged the text or widened its spacing, the band grows with it and the
   board moves down, rather than cutting the message off.
   The band keeps the shared 70ch measure for paragraphs (base.css), so on a
   wide screen it is narrower than the felt; `auto` side margins centre it
   over the board, as the board itself is centred. */
.table-felt[data-game="klondike"] .game-msg {
  min-height: 32px; margin: 0 auto; display: flex; align-items: center; justify-content: center;
  text-align: center; font-size: 0.8rem; line-height: 1.25; pointer-events: none;
}
.table-felt[data-game="klondike"] .sol-board { min-height: 0; }

/* The rail H1 carries the suit count ("Spider · 2 suits") or the draw rule
   ("Klondike · Draw 1"). Below 640 px it drops under the name in small type,
   so the H1 is no wider than the bare name; the separator stays in the
   accessible name. The rule stays stacked past the 560 px label breakpoint
   because the labelled tools are wider: inline, "Klondike · Draw 1" and the
   seven labelled tools need about 564 px of content, so from 560 px up to
   about 578 px (with a 15 px scrollbar) the row did not fit. Stacked, the
   H1 is about 96 px instead of 187, and the row fits from 560 px. */
.table-title .tt-sub { font-weight: 400; color: var(--table-stock); }
@media (max-width: 639.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; padding: 2px 0; }
}

/* #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; }
