@font-face {
  font-family: "Baloo 2";
  font-style: normal;
  font-weight: 800;
  font-display: optional;
  src: url("/fonts/baloo-2-800.woff2") format("woff2");
}

:root {
  --bg: oklch(0.985 0.01 85);
  --ink: oklch(0.22 0.02 60);
  --ink-2: oklch(0.5 0.02 60);
  --board: oklch(0.975 0.012 240);
  --dot: oklch(0.86 0.02 250);
  --dot-shape: oklch(0.7 0.05 250);
  --line: oklch(0.24 0.03 250);
  --border: oklch(0.9 0.02 80);
  --primary: oklch(0.64 0.21 45);
  /* Stroke widths are in SVG user units, where 1 unit = one board cell.
     0.28 puts the line at about 1.5x the mask dot's diameter (r 0.09), which
     is what makes the drawing read as line art rather than as marks on a dot
     grid. At 0.15 the line was thinner than the dots and the arrowheads
     dominated it. */
  --stroke: 0.28;
}

* {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  margin: 0;
}

body {
  display: grid;
  /* minmax(0, 1fr), not a bare 1fr: without the explicit 0 minimum, the
     column's automatic minimum size tracks #board's own JS-set pixel width,
     so shrinking the window never shrinks the column — it only grows to
     match whatever #board was last sized to, and the board (and the HUD
     buttons beside it) overflow off the right edge instead of fitting. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  background: var(--bg);
  color: var(--ink);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: 14px;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
}

.hud {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  background: #fff;
}

.hud-stats {
  display: flex;
  gap: 12px;
  color: var(--ink-2);
  white-space: nowrap;
}

.hud-stats b {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.hud-level {
  font-family: "Baloo 2", system-ui, sans-serif;
  font-weight: 800;
  font-size: 16px;
  color: var(--ink);
}

.hud-lives {
  color: var(--primary);
}

.hud-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

button {
  font: inherit;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: #fff;
  color: var(--ink);
  cursor: pointer;
}

button:focus-visible {
  outline: 3px solid var(--primary);
  outline-offset: 2px;
}

button[aria-pressed="true"] {
  background: var(--ink);
  color: #fff;
  border-color: var(--ink);
}

button.primary {
  background: var(--primary);
  border-color: transparent;
  color: #fff;
  padding: 10px 20px;
  font-size: 16px;
}

.board-wrap {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 0;
  padding: 10px;
}

/* One faint dot per cell, everywhere: the field the shape sits on. The tile
   is exactly one cell wide and `circle at center` puts the dot at the cell
   centre, which is where piece polylines have their vertices. This layer
   sits outside .camera's SVG transform, so --k (written by applyCamera in
   game.js) redoes that scaling here — without it the lattice stays at the
   1x tile size while the zoomed-in board content grows past it.

   overflow: hidden matters — a piece flying out keeps being drawn past the
   board edge until its tail clears, and without this it would paint over the
   padding and up into the HUD.

   touch-action: none is what lets the pinch and pan handlers in game.js see
   the gestures at all; without it the browser eats them as page zoom and
   scroll. Scoped to the board, so the rest of the page still scrolls. */
.board {
  position: relative;
  overflow: hidden;
  touch-action: none;
  border-radius: 14px;
  background-color: var(--board);
  background-image: radial-gradient(
    circle at center,
    var(--dot) 1.1px,
    transparent 1.2px
  );
  background-size: calc(100% / var(--n) * var(--k, 1))
    calc(100% / var(--n) * var(--k, 1));
}

.board svg {
  display: block;
  width: 100%;
  height: 100%;
}

.board.dragging {
  cursor: grabbing;
}

/* The shape layer. Without it the mask is invisible and the board reads as a
   handful of marks scattered on an empty field — fill is only 0.28 to 0.6, so
   the pieces alone never draw the outline. */
.mask circle {
  fill: var(--dot-shape);
}

.piece {
  cursor: pointer;
}

.piece .body {
  fill: none;
  stroke: var(--line);
  stroke-width: var(--stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* The head is a filled triangle, so it takes fill where the body takes
   stroke. Every rule that recolours a piece has to set both. */
.piece .cap {
  fill: var(--line);
  stroke: none;
}

/* Invisible fat stroke under the visible one: even a 0.28-unit line is only
   about 2.4px wide on a 40x40 board at phone width. */
.piece .hit {
  fill: none;
  stroke: transparent;
  stroke-width: 0.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.piece:focus {
  outline: none;
}

.piece:focus-visible .body,
.piece.hint .body {
  stroke: var(--primary);
  stroke-width: calc(var(--stroke) * 1.6);
}

.piece:focus-visible .cap,
.piece.hint .cap {
  fill: var(--primary);
}

.piece.shake {
  animation: shake 0.36s ease;
}

/* Units are SVG user units here, not px: the <g> lives inside the viewBox. */
@keyframes shake {
  0%,
  100% {
    translate: 0 0;
  }
  30% {
    translate: calc(var(--dx) * 0.16px) calc(var(--dy) * 0.16px);
  }
  65% {
    translate: calc(var(--dx) * -0.07px) calc(var(--dy) * -0.07px);
  }
}

.overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: oklch(1 0 0 / 0.86);
}

.overlay[hidden] {
  display: none;
}

.overlay-card {
  text-align: center;
  display: grid;
  gap: 8px;
  padding: 20px;
}

.overlay-title {
  margin: 0;
  font-family: "Baloo 2", system-ui, sans-serif;
  font-weight: 800;
  font-size: 28px;
}

.overlay-card p {
  margin: 0;
  color: var(--ink-2);
}

@media (prefers-reduced-motion: reduce) {
  .piece.shake {
    animation: none;
  }
}
