/* PuzzleFit styling, the same palette the other eight games use: a black page,
   the board on a lighter grey rounded panel, one light pill button, and that
   app's outline.

   The board colours are the PuzzleFit die colours themselves, flat and
   unshaded: amber, sky, orange-red, green, purple, and one pink added because
   a six-colour level needs a sixth and the palette only carries five. They are
   roles, not decoration — `--c0` is always the first chip on the tray — so they
   stay put in both themes the way the die colours do everywhere else.

   **The order is load-bearing.** A level takes the first `n` of them, so the
   four a `mini` or `easy` board is played on are the four furthest apart: warm
   light, cool light, warm dark, cool mid. Reordering this list changes which
   colours a four-colour board is asked to tell apart.

   They are solid on purpose, and that costs something worth stating: an earlier
   version wove a texture into each one so a player who cannot separate two hues
   still had a signal. Flat colour is the look that was asked for, so what
   carries that job now is the hairline between two vlakken (`--edge`, drawn per
   square by `edgesAt()`) and the spread in lightness across the palette — a
   vlak is still a shape with a border, not a wash of hue. */
:root {
  --bg: #0a0a0a;
  --board-panel: #242424;      /* the tray the board sits on */
  --card: #000000;             /* outlined cards */
  --outline: rgba(255, 255, 255, .92);
  --edge: rgba(0, 0, 0, .45);  /* the hairline between two vlakken */
  --accent: #ab8dfa;
  --bad: #ff5722;
  --light: #e3e3e3;            /* the primary button */
  --primary-text: #0a0a0a;     /* ...and the text on it */
  --text: #ffffff;
  --muted: #9b9b9b;

  --c0: #efbc4a;               /* --op      amber */
  --c1: #5cc8f7;               /* --player  sky */
  --c2: #ff5722;               /* --bad     orange-red */
  --c3: #4ed35e;               /* --tile    green */
  --c4: #ab8dfa;               /* --accent  purple */
  --c5: #f56aa8;               /* the sixth, in the same family */

  /* How long one square takes to take the ink, and how much later than its
     neighbour it starts. Published here because game.js writes the per-square
     delay and the stylesheet spends it — see `--ink-delay` below. */
  --ink-ms: 150ms;
}

/* A PGSDK host can report that it is running light; the board colours are the
   brand and stay put, everything around them inverts. Set by applyDarkMode(). */
body.light {
  --bg: #f1f1f4;
  --board-panel: #dededf;
  --card: #ffffff;
  --outline: rgba(0, 0, 0, .88);
  --edge: rgba(255, 255, 255, .75);
  --light: #14141a;
  --primary-text: #f4f4f6;
  --text: #101014;
  --muted: #5c5c66;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;                       /* the game owns the viewport */
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  -webkit-tap-highlight-color: transparent;
}

/* One screenful: the header takes what it needs, the tray is a fixed strip, and
   the board gets whatever is left. Anything added here comes straight off the
   board — put it in the tray or the overlay instead. */
.app {
  max-width: 460px;
  height: 100vh;
  height: 100dvh;
  margin: 0 auto;
  padding: max(10px, env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;                        /* narrow phones drop the tools to a second line */
}

.tools {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
  margin-left: auto;
}

[hidden] { display: none; }

select {
  background: var(--card);
  color: var(--text);
  border: 2px solid var(--outline);
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 15px;
  font-weight: 700;
}

.clock {
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* The moves left. It is the budget and the budget is the board's optimum, so
   this number going red is the game being lost rather than a warning. */
.left {
  font-size: 15px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.left.last { color: var(--bad); }

/* There is no pan and no zoom: every level fits, so the square size picked by
   fitBoard() is the whole layout. The one gesture on the board is a tap, which
   is why it never had to be shared with panning. */
.viewport {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;                          /* let it shrink inside the column */
  overflow: hidden;
  border-radius: 26px;
  background: var(--board-panel);
  touch-action: none;
  overscroll-behavior: contain;
  display: grid;
  place-items: center;
}

.stage { position: relative; }

/* The board's answer to a hint that has nothing to point at: the position can
   no longer be finished in the moves left, and what it wants is Terug. */
.stage.shake { animation: shake .3s; }
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* No gap between squares: a vlak is a run of squares of one colour and has to
   read as one shape, so the only thing drawn between two squares is the
   hairline game.js puts on the sides where the colour changes. */
.board {
  display: grid;
  grid-template-columns: repeat(var(--cols, 6), var(--cell));
  padding: var(--pad);
  border-radius: calc(var(--cell) * .3);
  overflow: hidden;
}

/* The ink is what makes a paint read as one thing happening rather than the
   board simply being different. Each square fades to the new colour over
   `--ink-ms`, starting `--ink-delay` after the tap — and game.js sets that
   delay to how many squares it is from the finger, so the colour runs outward
   through the vlak from where the bucket landed.

   The hairline is on the same transition and the same delay, so the seam
   between two vlakken that have just merged dissolves as the ink reaches it
   rather than a moment before or after. */
.cell {
  width: var(--cell);
  height: var(--cell);
  position: relative;
  transition: background-color var(--ink-ms) linear, box-shadow var(--ink-ms) linear;
  transition-delay: var(--ink-delay, 0ms);
}

/* The six roles. `--colour` is the same variable the tray chip and the splash
   read, so a chip, the ink and the vlakken it paints can never drift apart —
   and `background-color` is set to a flat colour rather than through that
   variable, because a transition between two concrete colours is the one thing
   every engine animates the same way. */
.c0 { --colour: var(--c0); background-color: var(--c0); }
.c1 { --colour: var(--c1); background-color: var(--c1); }
.c2 { --colour: var(--c2); background-color: var(--c2); }
.c3 { --colour: var(--c3); background-color: var(--c3); }
.c4 { --colour: var(--c4); background-color: var(--c4); }
.c5 { --colour: var(--c5); background-color: var(--c5); }

/* Where the bucket landed: a blot of the new colour thrown over the square the
   finger hit, spreading and thinning out. It is drawn over the neighbours
   rather than clipped to its own square — that overspill is what makes it read
   as ink hitting the board instead of a cell lighting up — and it never takes
   a hit test, because the viewport owns every gesture. */
.cell.splash::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: 50%;
  background: var(--colour);
  pointer-events: none;
  animation: splash 340ms ease-out forwards;
}

@keyframes splash {
  from { transform: scale(.2); opacity: .9; }
  to   { transform: scale(1.9); opacity: 0; }
}

/* A player who has asked not to be moved at gets the board without the spread:
   the colour is the information, the animation is only how it arrives. */
@media (prefers-reduced-motion: reduce) {
  .cell { transition: none; }
  .cell.splash::after { animation: none; }
  .chip.pulse { animation: none; }
  .stage.shake { animation: none; }
}

/* Where a hint is pointing. It marks the vlak and never paints it: any vlak the
   search names is on a line that still fits the budget, so finding one is the
   whole game and taking it would be a turn rather than a hint. */
.cell.hinted { box-shadow: inset 0 0 0 2px var(--accent) !important; }

/* Where the keyboard is standing. The pointer never needs it — a tap says
   where it means — so nothing shows until an arrow key is pressed. */
.cell.cursor { box-shadow: inset 0 0 0 2px var(--outline) !important; }

/* The palette. One chip per colour on the board, always the same count, so
   unlike a piece tray it never needs to scroll or wrap away. */
.tray {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 10px 8px;
  background: var(--card);
  border: 1px solid var(--outline);
  border-radius: 18px;
  touch-action: none;
}

.chip {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background-color: var(--colour);
  cursor: pointer;
  border: 2px solid transparent;
}

/* The colour a tap on the board will paint with. There is always exactly one,
   which is what lets the board be played with a single tap. */
.chip.picked {
  border-color: var(--outline);
  transform: scale(1.08);
}

.chip.pulse { animation: pulse .9s ease-in-out 2; }

@keyframes pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.16); }
}

.btn {
  background: var(--card);
  color: var(--text);
  border: 2px solid var(--outline);
  border-radius: 999px;
  padding: 16px 20px;
  font-size: 17px;
  font-weight: 700;
  cursor: pointer;
}

/* The header row: small enough that three of them cost the board a few pixels. */
.btn.mini {
  padding: 5px 9px;
  font-size: 12px;
  border-width: 1px;
}

.btn:active { transform: scale(.97); }
.btn.primary { background: var(--light); border-color: var(--light); color: var(--primary-text); }
.btn.hidden { display: none; }

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .72);
  display: grid;
  place-items: center;
  padding: 20px;
  z-index: 30;
}

.overlay.hidden { display: none; }

.panel {
  background: var(--card);
  border-radius: 22px;
  border: 2px solid var(--outline);
  padding: 26px 22px;
  text-align: center;
  max-width: 320px;
  width: 100%;
}

.panel h2 { margin: 0 0 8px; font-size: 28px; font-weight: 800; }
.panel p { margin: 0 0 18px; color: var(--muted); font-size: 15px; line-height: 1.5; }
.panel .btn { width: 100%; }
.panel .btn + .btn { margin-top: 10px; }
