/* Solitaire. Cards are the shared `card-` rules in cards.css; `sol-` rules
   are this game's. Everything is sized from --sol-w, one card width, so the
   seven columns fit a 390 px phone and cap at 5rem on a desktop. */
@import url("cards.css");

:root {
  --sol-gap: 0.3rem;
  --sol-pad: 0.4rem;
  --sol-hint: #f5d36b;
  /* How far each card in a column is offset, in card widths: face-down ones
     barely, face-up ones enough to read the corner. On a phone a card is 45 px
     wide, so 0.38 would be a 17 px strip; 0.7 is 31 px, as tall as the space
     allows without making a long run hard to scroll past. */
  --sol-down: 0.14;
  --sol-up: 0.38;
  /* The content width is the page width up to 46rem, less the page's own
     1rem gutters and the board's padding. Computed from the viewport and
     set on :root so a card being dragged (a clone on <body>) is the same
     size as the one it came from. */
  --sol-w: min(5rem, calc((min(46rem, 100vw) - 2rem - 2 * var(--sol-pad) - 6 * var(--sol-gap)) / 7));
}

/* As wide as the felt and no wider. The board centres its children, which
   shrink to their widest line, so a long score line ("+15, a card up and one
   turned over") once widened the whole block for a move and spread the
   columns apart; now the text wraps inside instead. */
.sol {
  position: relative; display: flex; flex-direction: column; gap: 0.7rem;
  width: calc(7 * var(--sol-w) + 6 * var(--sol-gap) + 2 * var(--sol-pad));
}
/* Room for the score line to wrap without moving the felt down a line. */
.sol-stats { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 0.2rem 1.2rem; font-weight: 700; min-height: calc(2lh + 0.2rem); }
@media (max-width: 30rem) { .sol-stats { min-height: calc(3lh + 0.4rem); } }
.sol-controls { display: flex; gap: 0.6rem; min-height: 2.4rem; align-items: center; }
.sol-controls.idle { min-height: 0; }

.sol-board {
  display: grid; grid-template-columns: repeat(7, var(--sol-w)); justify-content: space-between;
  gap: calc(var(--sol-w) * 0.3) var(--sol-gap); padding: var(--sol-pad);
  background: var(--felt); border-radius: var(--radius); box-shadow: var(--shadow);
}

.sol .card, .gn-drag-ghost .card { --card-w: var(--sol-w); }
.sol .card-corner { flex-direction: row; align-items: baseline; gap: 0.1em; top: 0.12rem; left: 0.2rem; }
.sol .card-suit { font-size: 1em; }
.sol .card-pip { padding-top: calc(var(--sol-w) * 0.3); font-size: calc(var(--sol-w) * 0.55); }

@media (max-width: 30rem) {
  :root { --sol-up: 0.7; }
}

/* Every card position is absolutely placed in its column or slot. A column
   sets --down and --up (how many cards of each kind); a face-down card has
   --k (its place among them) and a face-up one --r. */
.sol .sol-c {
  position: absolute; left: 0; top: 0;
  width: var(--sol-w); height: calc(var(--sol-w) * 1.4); box-sizing: border-box;
  display: block; padding: 0; margin: 0; border: 0; background: none; font: inherit; color: inherit;
}
.sol button.sol-c { cursor: pointer; }
.sol .sol-c .card { display: block; }
.sol .sol-c.card { display: inline-block; }
.sol .sol-c.card-back { cursor: default; }
.sol .sol-dn { top: calc(var(--k) * var(--sol-down) * var(--sol-w)); }
.sol .sol-up { top: calc((var(--down) * var(--sol-down) + var(--r) * var(--sol-up)) * var(--sol-w)); }
/* A face-up card is only as tall as the strip showing, so its button never
   sits under the next card's. Its face still spills over, below the next. */
.sol .sol-col > .sol-up:not(:last-child) { height: calc(var(--sol-up) * var(--sol-w)); }
/* A card being dragged is a clone on <body>, outside .sol: the whole card. */
.gn-drag-ghost.sol-c { height: calc(var(--sol-w) * 1.4) !important; padding: 0; border: 0; background: none; }

/* The felt is as tall as the longest column could ever be (six face-down
   cards under a whole run, king to ace), from the first deal on, so it
   never changes size as columns grow and shrink, and nothing below it
   moves (Jason, 2026-10-07: "presize it to max so it didn't keep changing
   sizes"). */
.sol-col {
  position: relative;
  min-height: calc((6 * var(--sol-down) + 12 * var(--sol-up) + 1.4) * var(--sol-w));
  height: calc((var(--down) * var(--sol-down) + max(var(--up) - 1, 0) * var(--sol-up) + 1.4) * var(--sol-w));
}

.sol-slot {
  border: 2px dashed rgb(255 255 255 / 0.3); border-radius: calc(var(--radius) * 0.7); box-sizing: border-box;
}
.sol-stock, .sol-waste, .sol-found {
  position: relative; display: block; height: calc(var(--sol-w) * 1.4); padding: 0; font: inherit; color: inherit;
}
.sol-stock { width: var(--sol-w); background: none; border: 0; }
button.sol-stock { cursor: pointer; }
.sol-waste { grid-column: span 2; }
.sol-waste .sol-c { left: calc(var(--x, 0) * var(--sol-w) * 0.5); }
.sol-found { width: var(--sol-w); }
.sol-found .sol-c { inset: 0; }
.sol-suit { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: calc(var(--sol-w) * 0.6); color: rgb(255 255 255 / 0.3); }

/* The shared dashed slot, lightened to show on felt. */
.sol .card-slot { border-color: rgb(255 255 255 / 0.3); }
/* Under the count pill, which cards.css sizes for a 4.4rem card: here the card
   may be 45 px wide, so the pill shrinks with it. */
.sol .card-count { min-width: 0; font-size: calc(var(--sol-w) * 0.38); padding: 0.05em 0.5em; }
.sol-recycle {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: calc(var(--sol-w) * 0.6); opacity: 0.7; color: var(--felt-ink);
}

/* What turning the waste over costs, on the stock itself: the count pill's
   shape at the foot of the slot, in the losing colour. */
.sol-cost {
  position: absolute; left: 50%; bottom: 0.15rem; transform: translateX(-50%);
  padding: 0.05em 0.4em; font-size: calc(var(--sol-w) * 0.3); font-weight: 800; line-height: 1.3;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--bad); background: var(--card); border-radius: 999px; box-shadow: var(--shadow);
}
/* The score's last change and why, quieter than the score. */
.sol-change { font-weight: 400; color: var(--ink-soft); }

/* An empty column. `.sol .sol-c` above removes borders, so this is as specific. */
.sol .sol-c.sol-slot { border: 2px dashed rgb(255 255 255 / 0.3); border-radius: calc(var(--radius) * 0.7); }

/* What you've picked up, and where it can go. */
.sol .sol-sel .card { box-shadow: 0 0 0 3px var(--accent), var(--shadow); border-color: var(--accent); }
.sol .sol-c[aria-label^="Move to"] .card { box-shadow: 0 0 0 3px var(--sol-hint), var(--shadow); }
.sol .sol-c.sol-slot[aria-label^="Move to"] { border-color: var(--sol-hint); background: rgb(245 211 107 / 0.2); }
.sol button.sol-c:hover .card, .sol button.sol-c:focus-visible .card { border-color: var(--accent); }
.sol button.sol-c:focus-visible, .sol button.sol-stock:focus-visible { outline: none; }
.sol button.sol-stock:focus-visible .card { box-shadow: 0 0 0 3px var(--accent); }
/* Every pile takes a drop (a refused one is answered with why), so marking
   them all while dragging would mark everything: only the one under the
   card is lit. Tap a card to see where it can go. */
.sol .gn-drop-over { outline: 2px solid var(--sol-hint); outline-offset: 1px; background: rgb(245 211 107 / 0.2); }
.sol .gn-dragging { opacity: 0.4; }
.sol button.sol-c[data-gn-drag] { cursor: grab; }

/* ---- The win cascade ---- */
/* 52 cards, two animations each, for 14 seconds, so what a frame costs
   matters more here than anywhere: measured with the CPU throttled 4x, a
   keyframe that reads a custom property (`translateY(var(--floor))`) made
   the browser redo style for every animated card on every frame, about
   three times what the same keyframes cost with fixed values, and the
   cascade stuttered at 8x. So the keyframes hold only fixed values, and
   the per-card and per-screen distances come from the box they move: a
   `.sol-fall` is as tall as the fall (--floor) and as wide as its sideways
   travel (--reach cards), and the keyframes move it by percentages of
   itself. The card sits in its top corner (top left going right, top
   right going left), so a box that moves 100% of its own size moves the
   card exactly that far. Keep any new keyframe free of var(). */
.sol-cascade {
  --cascade-h: min(70vh, 32rem);
  --floor: calc(var(--cascade-h) - var(--sol-w) * 1.4);
  position: absolute; left: 0; right: 0; top: 4rem; height: var(--cascade-h);
  overflow: hidden; pointer-events: none; z-index: 20;
}
.sol-fall {
  position: absolute; top: 0; left: calc(var(--sol-pad) + (3 + var(--s)) * (var(--sol-w) + var(--sol-gap)));
  display: flex; align-items: flex-start; width: calc(var(--reach) * var(--sol-w)); height: var(--floor); opacity: 0;
}
/* A card going left starts at the box's right edge, so the box can slide
   left over the distance it is wide. */
.sol-fall.sol-leftward { margin-left: calc(var(--sol-w) - var(--reach) * var(--sol-w)); justify-content: flex-end; }
/* The cascade is marked `gn-fresh` by the PlayOnce script inside it, and
   --played says how long ago the win was first seen: every push after the win
   redraws the cascade, and the negative delay makes it carry on from where it
   was instead of starting over, then show nothing once it's done. The sideways
   drift is the `translate` property and the bounce is `transform`, so the two
   run side by side on one box. */
.sol-cascade.gn-fresh .sol-fall {
  --delay: calc(var(--i) * 0.16s - var(--played, 0s));
  animation: sol-drift 5.5s linear var(--delay) both, sol-bounce 2.4s linear var(--delay) both;
}
.sol-cascade.gn-fresh .sol-fall.sol-leftward {
  animation-name: sol-drift-left, sol-bounce;
}

@keyframes sol-drift {
  0% { opacity: 0; translate: 0 0; }
  1% { opacity: 1; }
  85% { opacity: 1; }
  100% { opacity: 0; translate: 100% 0; }
}
@keyframes sol-drift-left {
  0% { opacity: 0; translate: 0 0; }
  1% { opacity: 1; }
  85% { opacity: 1; }
  100% { opacity: 0; translate: -100% 0; }
}
@keyframes sol-bounce {
  0% { transform: translateY(0); animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6); }
  30% { transform: translateY(100%); animation-timing-function: cubic-bezier(0, 0.4, 0.5, 1); }
  48% { transform: translateY(50%); animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6); }
  64% { transform: translateY(100%); animation-timing-function: cubic-bezier(0, 0.4, 0.5, 1); }
  76% { transform: translateY(78%); animation-timing-function: cubic-bezier(0.5, 0, 1, 0.6); }
  86% { transform: translateY(100%); }
  100% { transform: translateY(100%); }
}
@media (prefers-reduced-motion: reduce) {
  .sol-cascade { display: none; }
}

.sol-config { border: 1px solid var(--line); border-radius: var(--radius); padding: 0.5rem 0.75rem; margin-bottom: 0.6rem; }
.sol-config label { display: block; padding: 0.15rem 0; }
