/* The 404 game.
 *
 * A separate stylesheet rather than lines added to site.css, because none of
 * this is reused anywhere: one page, one board, and it should be trivial to
 * delete the whole feature by removing two <link>/<script> tags.
 *
 * 🔴 The <link> and <script> to this file and its sibling carry a `?v=` query,
 * and they have to. nginx serves /assets/ with `Cache-Control: max-age=604800`
 * on fingerprint-free filenames — a deliberate trade the server block documents
 * — so an edit here reaches a RETURNING visitor up to a week late. Measured:
 * after deploying a layout fix, the server had the new file and the browser
 * was still laying the page out with the old one. Bump the query when you edit
 * either file.
 *
 * Every colour is a token from site.css. Nothing here introduces a new hue —
 * the board is the site's own palette rearranged, which is also why it needs no
 * separate contrast pass: --trace, --ember and --violet are already validated
 * as graphics on --bg, and every piece of TEXT is --ink or --ink-2.
 */

/* 🔑 Tight, because the game is the point of this page and it was landing
   below the fold. Measured on a 901x1014 window before this: the board's top
   edge sat at y=606, so a visitor met a large "404" and had to scroll to find
   out anything was there. The headline still reads first; it just stops
   occupying half the screen to do it. */
.nf-hero {
  text-align: center;
  padding-block: var(--s-lg) var(--s-xs);
}

.nf-code {
  font-size: clamp(2.75rem, 9vw, 4rem);
  line-height: 1;
  font-weight: 800;
  letter-spacing: -0.03em;
  margin: 0;
  background: linear-gradient(135deg, var(--cta-from), var(--cta-to));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* 🔑 A fallback colour under the gradient text. `background-clip: text` with a
     transparent foreground renders NOTHING at all in a browser that does not
     support it — the headline would simply be missing rather than plain. */
  -webkit-text-fill-color: transparent;
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .nf-code {
    color: var(--accent);
    -webkit-text-fill-color: currentColor;
  }
}

.nf-lede {
  color: var(--ink-2);
  max-width: var(--measure);
  margin: var(--s-md) auto 0;
}

/* ---- the board ------------------------------------------------------- */

/* Capped by HEIGHT as well as width. The board is square, so a width-only cap
   makes it as tall as it is wide — 560px of board under a headline is most of a
   laptop screen. `min()` against the viewport keeps the whole thing visible on
   a short window without changing anything on a phone, where 92vw wins. */
.nf-game {
  max-width: min(440px, 92vw, 52vh);
  margin: var(--s-lg) auto 0;
}

.nf-board-wrap {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-card);
  /* A hint that the board is the interactive thing on the page. */
  cursor: pointer;
}

/* A square board at every width. `aspect-ratio` on the WRAPPER rather than on
   the canvas: the canvas has a fixed pixel backing store and letting CSS size
   it independently is what makes a game look soft on a retina screen. */
.nf-board-wrap::before {
  content: "";
  display: block;
  padding-top: 100%;
}

.nf-board {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;   /* swipes steer; they must not scroll the page */
}

/* The overlay that carries every message the game has to give: the invitation
   to start, and the score at the end. One element, three states, so a state
   can never be shown while another is still on screen. */
.nf-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-sm);
  padding: var(--s-lg);
  text-align: center;
  background: color-mix(in srgb, var(--bg-sunken) 82%, transparent);
  backdrop-filter: blur(2px);
}

.nf-overlay[hidden] { display: none; }

.nf-overlay h2 {
  margin: 0;
  font-size: 1.15rem;
}

.nf-overlay p {
  margin: 0;
  color: var(--ink-2);
  font-size: 0.95rem;
  max-width: 30ch;
}

.nf-scoreline {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-md);
  margin-top: var(--s-sm);
  color: var(--ink-3);
  font-size: 0.9rem;
}

.nf-score {
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  font-weight: 700;
}

.nf-hint {
  color: var(--ink-3);
  font-size: 0.85rem;
  text-align: center;
  margin-top: var(--s-sm);
}

/* ---- the way out ------------------------------------------------------ */

.nf-exits {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-sm);
  justify-content: center;
  margin-top: var(--s-xl);
}

/* 🔑 Reduced motion does not stop the game — a game is motion the visitor asked
   for by pressing a key. It stops the DECORATION: the idle drift behind the
   board, which nobody asked for and which is exactly the kind of ambient
   movement that triggers vestibular symptoms. */
@media (prefers-reduced-motion: reduce) {
  .nf-board-wrap { box-shadow: none; }
}

/* ---------------------------------------------------------------------------
   The direction pad.

   🔴 Shown only on a coarse pointer, and it is the difference between this
   page being playable on a phone and merely being *reachable* from one. The
   board has always accepted a swipe — and never said so anywhere: the caption
   read "steer with the arrow keys", which is advice a phone cannot take. A
   control nobody can discover is a control nobody has.

   `pointer: coarse` rather than a width query: the question is whether the
   visitor has a fingertip or a cursor, not how wide their window is. A small
   desktop window keeps the keyboard; an iPad in landscape gets the pad.
   --------------------------------------------------------------------------- */
.nf-pad { display: none; }

@media (pointer: coarse) {
  .nf-pad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    gap: var(--s-xs);
    width: min(15rem, 70%);
    margin: var(--s-md) auto 0;
    /* The pad is chrome, not content: it must never take the swipe that the
       board is listening for, nor scroll the page under a thumb. */
    touch-action: manipulation;
  }

  .nf-key {
    /* 44 px is Apple's minimum comfortable target and the reason this is not
       prettier and smaller. */
    min-height: 3rem;
    font: 600 1.1rem/1 system-ui, -apple-system, sans-serif;
    color: var(--text);
    background: var(--bg-sunken);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
  }

  .nf-key:active { background: var(--line); }

  .nf-key-up    { grid-column: 2; grid-row: 1; }
  .nf-key-left  { grid-column: 1; grid-row: 2; }
  .nf-key-down  { grid-column: 2; grid-row: 2; }
  .nf-key-right { grid-column: 3; grid-row: 2; }
}
