/* ============================================================
   sol-core.css — the table every HACKING PARADISE card game sits
   on. Pulled out of solitaire.css on 2026-08-27 while building
   FreeCell.

   The card LOOK is entirely in hp-cards.css. Nothing here paints a
   card, it only places them, measures them, drags them, and draws
   the two bars that tell a player where the deal stands.

   Sizing is done in JS, because a cascade of nineteen cards has to
   shrink its own overlap to fit the screen and CSS cannot measure a
   pile. JS writes --cw, --ch, --gap and --fan on .board and every
   rule below is expressed in those. The one thing a game must set
   for itself is how many columns its lanes are, because that is the
   only number that differs.

   NOTE, 2026-08-27. Klondike still carries its own copy of these
   rules in solitaire.css. That is debt and it is on the board. Do
   not fork this file, move Klondike onto it.
   ============================================================ */

.gamewrap{max-width:1080px; margin:0 auto; padding:0 clamp(8px,2vw,20px)}

/* ---------- controls ---------- */
.controls{
  display:flex; flex-wrap:wrap; gap:8px; align-items:center;
  padding:10px 0 12px;
}
.controls .spacer{flex:1 1 auto}
.seg{display:inline-flex; border:1px solid var(--line); border-radius:999px; overflow:hidden;
  background:var(--panel)}
.seg button{
  font-family:var(--mono); font-size:.72rem; letter-spacing:.05em;
  background:transparent; border:0; color:var(--ink); cursor:pointer;
  min-height:var(--tap); padding:0 14px; line-height:1.1}
.seg button[aria-pressed="true"]{background:var(--accent); color:#15102a; font-weight:700}
:root[data-theme="light"] .seg button[aria-pressed="true"]{color:#f6f2ff}
.seg button:focus-visible{outline:3px solid var(--accent-ink); outline-offset:-3px}
.stat{font-family:var(--mono); font-size:.72rem; letter-spacing:.08em; color:var(--muted);
  white-space:nowrap}
.stat b{color:var(--ink); font-weight:700; font-variant-numeric:tabular-nums}

/* ---------- board ----------
   A game sets --cols on .board for its own lane width. Eight for
   FreeCell, seven for Klondike. Nothing else here cares. */
.board{
  --cw:60px; --ch:84px; --gap:6px; --cols:8;
  position:relative;
  padding:10px 0 40px;
  touch-action:pan-y;      /* vertical page scroll still works, drags do not scroll */
}
.board .lane{
  display:grid; grid-template-columns:repeat(var(--cols), var(--cw));
  gap:var(--gap); justify-content:center;
}
.top{margin-bottom:calc(var(--gap) * 2)}

/* one pile position. Cards inside are absolutely placed. */
.pile{position:relative; width:var(--cw); height:var(--ch)}
.pile .hpc{position:absolute; left:0; top:0; width:var(--cw); height:var(--ch)}
.pile.col{height:auto; min-height:var(--ch)}
.pile.spacer{visibility:hidden}

/* A card owns its own gestures. Without this the browser starts scrolling the
   page the moment a drag goes vertical and cancels the pointer mid move. The
   board shrinks its cascades to fit the screen, so vertical scrolling on the
   board itself is rarely needed. */
.board .hpc{touch-action:none; cursor:pointer}
.board .hpc.empty{cursor:default}
body.dragging, body.dragging *{cursor:grabbing !important}

/* the drag layer */
.draglayer{position:fixed; inset:0; z-index:200; pointer-events:none}
.draglayer[hidden]{display:none}
.draglayer .hpc{position:absolute; left:0; top:0; width:var(--dcw); height:var(--dch);
  box-shadow:0 10px 30px rgba(0,0,0,.6)}

/* ---------- win ---------- */
.winbar{
  margin:14px 0 0; border:1px solid var(--accent-ink); border-radius:14px;
  padding:16px 18px; background:var(--panel);
  display:flex; flex-wrap:wrap; gap:12px; align-items:center; justify-content:space-between;
}
.winbar[hidden]{display:none}
.winbar h2{margin:0; font-size:1.15rem}
.winbar p{margin:4px 0 0; color:var(--muted); font-size:.92rem}

/* ---------- stuck ----------
   Same shape as the win bar, deliberately not the same color. Green is the
   climb finishing. A dead deal is a different thing and should not wear the
   same badge. */
.stuckbar{
  margin:14px 0 0; border:1px solid var(--line); border-radius:14px;
  padding:16px 18px; background:var(--panel);
  display:flex; flex-wrap:wrap; gap:12px; align-items:center; justify-content:space-between;
}
.stuckbar[hidden]{display:none}
.stuckbar h2{margin:0; font-size:1.15rem}
.stuckbar p{margin:4px 0 0; color:var(--muted); font-size:.92rem; max-width:52ch}

/* ---------- hint ----------
   The ring is an outline rather than a border so it cannot move a card by a
   pixel, and outlines are never clipped by the pile above them. Solid is where
   the card comes from, dashed is where it can go. */
.pile.hint-src, .pile.hint-dst{border-radius:8px}
.pile.hint-src{outline:3px solid var(--accent-ink); outline-offset:3px}
.pile.hint-dst{outline:3px dashed var(--accent-ink); outline-offset:3px}
@media (prefers-reduced-motion: no-preference){
  .pile.hint-src, .pile.hint-dst{animation:hintpulse 1.1s ease-in-out 3}
}
@keyframes hintpulse{
  0%,100%{outline-offset:3px}
  50%{outline-offset:7px}
}
/* On a phone the outermost pile sits about 8px from the edge, and the ring is
   drawn outside the pile, so a 7px pulse plus a 3px stroke has its right edge
   shaved off. An outline never adds to scroll width, so nothing catches this
   except measuring the ring itself. Pull it in at narrow widths. It stays
   outside the card, which is the part that matters, it just breathes less. */
@media (max-width: 430px){
  .pile.hint-src, .pile.hint-dst{outline-offset:2px}
  @keyframes hintpulse{
    0%,100%{outline-offset:2px}
    50%{outline-offset:4px}
  }
}

/* ---------- rules ---------- */
.rules{margin-top:26px}
.rules summary{
  font-family:var(--mono); font-size:.74rem; letter-spacing:.08em; text-transform:uppercase;
  color:var(--accent); cursor:pointer; min-height:var(--tap); display:flex; align-items:center;
  gap:9px; list-style:none}
.rules summary::-webkit-details-marker{display:none}
/* display:flex eats the native marker, so draw one back. Without it the row
   reads as a heading and nobody knows it opens. */
.rules summary::before{content:"▸"; font-size:.9rem; transition:transform .18s ease}
.rules[open] summary::before{transform:rotate(90deg)}
.rules summary:focus-visible{outline:3px solid var(--accent-ink); outline-offset:2px}
.rules ul{margin:10px 0 0; padding-left:20px; color:var(--muted); max-width:62ch}
.rules li{margin:6px 0}

/* live region for screen readers, never visible */
.live{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0)}
