/*
 * Game-board geometry. Cards are sized from the page's dimensions with
 * container query units, piles sit on a grid, and fanned piles offset their
 * cards from counts that the game script publishes:
 *   on a fanned pile: --n-down, --n-up   face-down / face-up cards in it
 *   on each card:     --down, --up       face-down / face-up cards above it
 */

/* Registered so they compute to absolute lengths on .table and inherit as
   such; unregistered, `cqw`/`cqh` would re-resolve against each pile. */
@property --card-w { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --card-h { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --gap { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --fan-down { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --fan-up { syntax: '<length>'; inherits: true; initial-value: 0px; }

/* The page is the size container. Inside it, the table holds the bars and
   the board, and sets the card sizes both use: the bars are exactly as wide
   as the playing field. */
.game-page {
  height: 100dvh;
  container: page / size;
}

.table {
  --cols: 7;
  --card-aspect: 1.4; /* the card sprite's 250×350 viewBox */
  --max-card-w: 130px;
  --gap-ratio: 0.12; /* of card width */
  --fan-down-ratio: 0.1; /* of card height */
  --fan-up-ratio: 0.28;
  /* The bars' fixed heights (css/game.css), taken off the height for cards. */
  --nav-h: 2.5rem;
  --bar-h: 3.25rem;
  --chrome-h: calc(var(--nav-h) + var(--bar-h));
  /* Cards are sized so the top row plus a column of 6 face-down and 6 face-up
     cards fits; longer tableau columns are squeezed (see .tableau below). */
  --design-column: calc(1 + 6 * var(--fan-down-ratio) + 6 * var(--fan-up-ratio));

  --card-w: round(down, min(
    100cqw / (var(--cols) + (var(--cols) + 1) * var(--gap-ratio)),
    (100cqh - var(--chrome-h)) / (var(--card-aspect) * (1 + var(--design-column)) + 3 * var(--gap-ratio)),
    var(--max-card-w)
  ), 1px);
  --card-h: round(var(--card-w) * var(--card-aspect), 1px);
  --gap: round(down, var(--card-w) * var(--gap-ratio), 1px); /* down, so the field never outgrows the page */
  --fan-down: round(var(--card-h) * var(--fan-down-ratio), 1px);
  --fan-up: round(var(--card-h) * var(--fan-up-ratio), 1px);
  /* The playing field: the columns and the gaps around them. */
  --field-w: calc(var(--cols) * var(--card-w) + (var(--cols) + 1) * var(--gap));

  display: grid;
  grid-template-rows: auto 1fr;
  height: 100%;

  & main {
    min-height: 0;
    padding: 0;
  }
}

/* Phones in portrait are width-starved: tighter gaps (but enough to keep
   neighbouring cards visibly apart). Their fan shows 27% of each card, enough
   for the large-index faces' rank and suit (top 86 of 350 units,
   scripts/build-card-art.mjs); there's height to spare below. */
@container page (orientation: portrait) {
  .table {
    --gap-ratio: 0.32;
    --fan-up-ratio: 0.27;
  }
}

.board {
  display: grid;
  grid-template-columns: repeat(var(--cols), var(--card-w));
  grid-template-rows: var(--card-h) 1fr;
  justify-content: center;
  gap: var(--gap);
  padding: var(--gap);
  height: 100%;
  overflow: hidden;
}

.pile {
  position: relative;
  min-height: 0;
  /* A width container, so cards can pick their face design by size (below). */
  container-type: inline-size;
}

/* Every face carries two designs (scripts/build-card-art.mjs): classic pips
   and corner indices, and large-index faces (big rank and suit) for reading
   small cards. Piles narrower than this, i.e. small cards (phones, short
   landscape screens), show the large-index design. */
@container (width < 80px) {
  .card {
    --card-face-classic: none;
    --card-face-large: inline;
  }
}

/* Outline marking where an empty pile is. */
.stock,
.waste,
.foundation {
  &::before {
    content: '';
    position: absolute;
    inset: 0;
    border: 2px solid rgb(255 255 255 / 0.25);
    border-radius: calc(var(--card-w) * 0.072);
  }
}

/* The redeal arrow: only on an empty stock with cards on the waste beside it,
   when a tap turns the waste back over into the stock. */
.stock .redeal {
  display: none;
  position: absolute;
  inset: 0;
  width: 65%;
  margin: auto;
  color: white;
  opacity: 0.6; /* on the whole icon, so the overlapping arrowhead and arc stay one tone */
}

.stock:not(:has(> .card)):has(+ .waste > .card) .redeal {
  display: block;
}

/* Cards buried exactly under another are hidden: otherwise their faint edge
   strokes add up to a dark rim around a tall stack. What stays visible: the
   waste's top three (Draw 3 fans them), and a foundation's top two (so the
   card beneath shows while the top one is dragged off). Nothing is hidden
   while a card is still sliding onto the pile, or the cards it will cover
   would vanish before it arrives. */
.stock:not(.lifted) > .card:not(:last-child),
.foundation:not(.lifted) > .card:not(:nth-last-child(-n + 2)),
.waste:not(.lifted) > .card:not(:nth-last-child(-n + 3)) {
  visibility: hidden;
}

/* Draw 3 fans the top waste cards rightward (`--spread` 0, 1, 2) into the
   empty grid cell beside the waste. */
.waste > .card {
  translate: calc(var(--spread, 0) * var(--card-w) * 0.24) 0;
}

/* Tableau columns fan downward, squeezing to fit the column's height:
   face-down offsets shrink first (to 4% of a card), then face-up ones. */
.tableau {
  container-type: size;
  --n-down: 0;
  --n-up: 0;

  & > .card {
    --room: calc(100cqh - var(--card-h));
    --fd: clamp(
      var(--card-h) * 0.04,
      (var(--room) - max(var(--n-up) - 1, 0) * var(--fan-up)) / max(var(--n-down), 1),
      var(--fan-down)
    );
    --fu: clamp(
      0px,
      (var(--room) - var(--n-down) * var(--fd)) / max(var(--n-up) - 1, 1),
      var(--fan-up)
    );
    translate: 0 calc(var(--down, 0) * var(--fd) + var(--up, 0) * var(--fu));
  }
}

/* Interaction states, set by klondike/input.ts and klondike/view.ts. */
.board {
  touch-action: none; /* the board's pointer input is ours, not page scrolling */
}

.card.dragging {
  z-index: 20;
  pointer-events: none; /* so hit-testing finds the pile underneath */
}

.card.moving {
  z-index: 10;
}

/* Tableau columns are size containers, which makes each its own stacking
   context; lift the pile holding a travelling card so the card passes over
   the other piles, not under them. */
.pile.lifted,
.pile:has(> .card.dragging) {
  z-index: 1;
}

/* Klondike: stock, waste, a gap, then four foundations; seven tableau columns below. */
.table.klondike {
  --cols: 7;

  & .waste + .foundation {
    grid-column-start: 4;
  }
}
