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

   Arrows is the monochrome one. The pieces are drawn in `--text` on the panel —
   black on white in a light host, white on near-black by default — because the
   board is already busy with three dozen bent lines and colouring them would say
   something the game does not mean. Colour is kept for the two things that are
   feedback and nothing else: orange-red `--bad` for an arrow that cannot go and
   for what is in its way, purple `--accent` for the arrow a hint or the keyboard
   is pointing at. Green stays off the board entirely, exactly as it does in the
   other three. */
:root {
  --bg: #0a0a0a;
  --board-panel: #242424;      /* the tray the dice sit on */
  --card: #000000;             /* outlined cards */
  --outline: rgba(255, 255, 255, .92);
  --dot: rgba(255, 255, 255, .22);  /* an empty square, so the lattice shows */
  --op: #efbc4a;
  --player: #5cc8f7;
  --tile: #4ed35e;
  --accent: #ab8dfa;
  --bad: #ff5722;
  --light: #e3e3e3;            /* the primary button */
  --primary-text: #0a0a0a;     /* ...and the text on it */
  --text: #ffffff;
  --muted: #9b9b9b;
}

/* A PGSDK host can report that it is running light; the die colours are the
   brand and stay put, everything around them inverts. Set by applyDarkMode().
   Here that also flips the pieces themselves, which is the spec's board — black
   arrows on white — reached from the other side. */
body.light {
  --bg: #f1f1f4;
  --board-panel: #ffffff;
  --card: #ffffff;
  --outline: rgba(0, 0, 0, .88);
  --dot: rgba(0, 0, 0, .20);
  --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 board gets the rest. */
.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: 12px;
}

.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);
}

/* How many arrows are still on the board — the one number that says how far
   along you are, since every arrow leaves exactly once. */
.left {
  font-size: 14px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--primary-text);
  background: var(--light);
  border-radius: 999px;
  padding: 2px 10px;
}

/* The board is laid out at a fixed cell size; the viewport transform below is
   what scales it to fit, so type never has to be recomputed. */
.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;
}

.viewport.panning { cursor: grabbing; }

.stage {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.stage.animating { transition: transform .22s ease; }

/* game.js publishes the geometry as --cell and --pad on the stage and undoes
   exactly this arithmetic in cellAt(); the SVG's viewBox is the same sum in
   grid units. One place owns where a square is. */
.board {
  width: calc(var(--cols) * var(--cell) + 2 * var(--pad));
  height: calc(var(--rows) * var(--cell) + 2 * var(--pad));
}

.board svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;                      /* a piece on its way out crosses the wall */
  pointer-events: none;                   /* the viewport handles every gesture */
}

/* An empty square: a faint dot, so the lattice stays readable under the arrows
   and an empty corridor is something you can see rather than infer. */
.dot { fill: var(--dot); }

/* One piece: a heavy rounded stroke and a solid triangular head, both taking
   their colour from the group so a state change is one class. */
.piece { color: var(--text); }
.piece .body { fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; }
.piece .tip { fill: currentColor; }

/* Tapped, and it cannot go: the arrow and whatever is in its way both say so. */
.piece.blocked, .piece.blocker { color: var(--bad); }

/* The keyboard's selection, and the arrow a hint has just pointed at. */
.piece.pulse, .piece.hinted { color: var(--accent); }

/* Roughly 6px at the shipped cell size, along the axis the piece would travel,
   so a refused tap reads as "it tried and could not" rather than as nothing. */
.piece.shake.across { animation: shake-x .3s; }
.piece.shake.down { animation: shake-y .3s; }
@keyframes shake-x {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-0.15px); }
  75% { transform: translateX(0.15px); }
}
@keyframes shake-y {
  0%, 100% { transform: translateY(0); }
  25% { transform: translateY(-0.15px); }
  75% { transform: translateY(0.15px); }
}

/*
 * The way out. The body is drawn as a window on a longer path — its own bends
 * followed by the straight run off the board — so pulling the dash offset along
 * that path *is* the train: the head leads, every other cell arrives where its
 * successor just was, and the tail is last through the wall. The head is a
 * separate shape and simply slides along the straight part.
 *
 * The duration is set per piece in game.js, from how far it has to go.
 */
.piece.leaving .body { transition: stroke-dashoffset var(--exit) cubic-bezier(.22, .61, .36, 1); }
.piece.leaving .tip { transition: transform var(--exit) cubic-bezier(.22, .61, .36, 1); }

.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 two 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;
}

.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; }
