/* BrightKidz Hub — games: THE CONSTRUCTION SHEET
   =========================================================================

   THESIS. A game is a specimen sheet with its construction grid left showing.
   Every piece, every quantity and every path is built on one visible cellular
   armature, and the armature is not cleaned up afterwards — it is the proof of
   how the thing was made. This refuses what the games shipped first and what
   the category always ships: a small illustrated diagram parked above a row of
   grey buttons and a paragraph of instructions.

   OWN-WORLD. Board stock ruled with a faint construction grid. The subject ink
   owns whole planes — the sheet header, the active cell, the filled meter —
   never a lone tinted node. Structure is drawn twice, at two weights: hairline
   for the armature, 3px for anything you can touch. Quantity is never a
   numeral; it is CELLS FILLED, so a child reads amount as length. Pieces are
   die-cut: 4px corners, hard 2px offset shadow, no blur. One diagonal per
   sheet marks whose turn it is, and it is the only diagonal the system allows.

   STORY. A child sees ruled paper with a shape being built on it. They press a
   cell; it fills, and the meter beside it grows by one cell. Nothing is a
   number they must decode — the size of the thing IS the amount.

   FIRST VIEWPORT. The sheet fills the mount edge to edge and breaks out of the
   reading measure. Turn cells top-left, board centred and LARGE, meters and
   tray on the armature beside it, status printed along the bottom edge. Title
   and prose sit BELOW the board: the artifact leads.

   FORM. Crouwel grid specimen — the surviving challenger, owner-chosen over
   the assigned punch-out sheet (seed 8ab0aee0), staged on board stock rather
   than Crouwel's stark white because PRODUCT.md pins the warm ground and the
   six subject inks as brand commitments.

   -------------------------------------------------------------------------
   WHAT THIS FILE MAY AND MAY NOT DO

   Loaded ONLY on pages carrying a game — six game pages, the games index, and
   the six articles that mount one. Not on all 84. It is separate from site.css
   for that reason, and because site.css is shared ground several agents edit.

   THE CLASS CONTRACT IS CLOSED. Games use these and add none:
     .game-mount .game-intro .game-stagebar .game-board .game-grid .game-tray
     .game-controls .game-btn .game-status .game-cell .game-tile .game-readout
     .game-note
   plus, new with this world and available to every game:
     .game-meter / .game-meter-cell   quantity as cells filled, never a numeral
     .game-sheet-label                the small tracked label naming a region

   Colour comes from the ink tokens and nothing else, so a game is in its
   subject's colour with no per-game CSS. `--ink` is bound by data-subject on
   the page root, exactly as every other component works.

   Every state that uses colour ALSO uses shape or text: some of these readers
   are colour-blind, and a child cannot tell "I am bad at this" from "this is
   unfair" (DESIGN.md accessibility floor). */

/* ---------------------------------------------------------------- tokens -- */

.game-mount {
  /* Derived from the ink so a game never introduces a colour. */
  --sheet-rule: color-mix(in srgb, var(--ink) 22%, transparent);
  --sheet-rule-strong: color-mix(in srgb, var(--ink) 42%, transparent);
  --cell: 48px;          /* the armature's unit AND the tap-target floor */
}

/* ----------------------------------------------------------- the sheet -- */

.game-mount {
  margin: 2.4rem 0;
  border: var(--stroke) solid var(--rule);
  border-radius: var(--die-cut);
  background: var(--stock);
  box-shadow: var(--shadow);
  padding: clamp(.9rem, 3vw, 1.5rem);

  /* THE BOARD BREAKS OUT OF THE READING MEASURE.
     An article's <main> caps at ~676px, which starved the board: measured, a
     12-column game asked for 595px and was offered 569, so its cells missed
     the 48px floor by 2px even on a 1280px screen. The mount said
     `max-width: none` but the PARENT was the cap, so that intent was never
     delivered. A game is an artifact, not a paragraph; it gets the width.

     THE BREAKOUT IS BOUNDED BY MEASURED PADDING, NOT GUESSED.
     Measured on the built pages: <main> pads 16px at 320-390px and 27-28px at
     900-1280px. A first cut asked for 2rem (32px) each side — more padding
     than exists — and put an 11px horizontal scrollbar on the page at 900px.
     A vw-based clamp does not rescue it either, because vw includes the
     scrollbar. So the breakout never exceeds the smallest padding at its own
     breakpoint. */
  width: calc(100% + 2rem);
  margin-inline: -1rem;
  max-width: none;
}

@media (min-width: 48rem) {
  .game-mount { width: calc(100% + 3rem); margin-inline: -1.5rem; }
}

/* The construction grid, left showing.
   A repeating-linear-gradient here is RULING, not a gradient standing in for
   artwork (which DESIGN.md prohibits) — it is the same device as the printed
   grid on graph paper, and it is what makes every alignment in the game
   legible as deliberate. */
.game-mount[data-bk-state="live"] {
  background-image:
    repeating-linear-gradient(to right,
      var(--sheet-rule) 0 1px, transparent 1px var(--cell)),
    repeating-linear-gradient(to bottom,
      var(--sheet-rule) 0 1px, transparent 1px var(--cell));
  background-position: 0 0;
}

/* ONCE THE GAME IS LIVE, THE BOARD COMES FIRST.
   Measured at 320px: several fallbacks ran to three paragraphs and pushed the
   board off the bottom of a phone — the "instructions first" anti-pattern, and
   a seven-year-old will not read them. Ordering rather than HIDING: the prose
   is the no-JS document and carries real content, so it moves below the board
   rather than disappearing. With JavaScript off the rule never applies. */
.game-mount[data-bk-state="live"] {
  display: flex;
  flex-direction: column;
}
.game-mount[data-bk-state="live"] .game-intro {
  order: 99;
  margin: 1.6rem 0 0;
  padding-top: 1.1rem;
  border-top: 1px solid var(--sheet-rule-strong);
  font-size: .95rem;
}

.game-intro {
  margin: 0 0 1rem;
  max-width: var(--measure);
}
.game-intro > :first-child { margin-top: 0; }
.game-intro > :last-child { margin-bottom: 0; }
.game-mount[data-bk-state="inert"] .game-intro { margin-bottom: 0; }

/* The small tracked label that names a region. Crouwel's specimen sheets name
   their parts; so does a rulebook. */
.game-sheet-label {
  display: block;
  font-family: var(--font-read);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-edge);
  margin: 0 0 .4rem;
}

/* ------------------------------------------------------------ the turns -- */

/* Turn markers are CELLS of the armature, filled. Not pills: a pill is a
   generic chip, a filled cell is this system saying "one unit of the grid". */
.game-stagebar {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: 0 0 1.2rem;
  padding: 0;
  list-style: none;
  font-size: .8rem;
  font-weight: 700;
  border: 1px solid var(--sheet-rule-strong);
  width: fit-content;
  max-width: 100%;
  background: var(--stock);
}
.game-stagebar li {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  padding: .5rem .8rem;
  min-height: 40px;
  border-right: 1px solid var(--sheet-rule-strong);
  color: var(--board);
}
.game-stagebar li:last-child { border-right: 0; }

/* THE CURRENT TURN IS AN INK PLANE with a die-cut corner.
   Colour is never the only signal: the notch is a shape, and the item is also
   the only one marked aria-current for a screen reader.

   ⚠ The corner, NOT a diagonal across the cell. The first cut ran the system's
   permitted diagonal corner-to-corner and it read as a STRIKETHROUGH — "1.
   Find a way" looked cancelled, which is the opposite of "you are here". A
   device that says the wrong thing loudly is worse than no device. The
   diagonal survives as the cut corner, where it marks the piece without
   crossing its label. */
.game-stagebar li[aria-current="step"] {
  background:
    linear-gradient(to bottom left,
      var(--stock) 0 9px, transparent 9px),
    var(--ink);
  color: var(--stock);
}
.game-stagebar li[data-done="yes"]::before { content: "\2713\00a0"; }

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

.game-board {
  display: block;
  margin: 0 0 1.1rem;
  padding: clamp(.5rem, 2vw, 1rem);
  border: 1px solid var(--sheet-rule-strong);
  background: var(--stock);
  overflow-x: auto;              /* a board never makes the PAGE scroll */
  -webkit-overflow-scrolling: touch;
}
.game-board svg { display: block; max-width: 100%; height: auto; margin: 0 auto; }

.game-grid {
  display: grid;
  gap: 4px;
  margin: 0 auto;
  width: max-content;
  max-width: 100%;
}

/* --------------------------------------------------------------- pieces -- */

.game-cell,
.game-tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--cell);
  min-height: var(--cell);
  padding: 0;
  font: inherit;
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--board);
  background: var(--stock);
  border: var(--stroke) solid var(--rule);
  border-radius: var(--die-cut);
  cursor: pointer;
  transition: transform .14s cubic-bezier(.16, 1, .3, 1),
              background-color .14s ease;
}
.game-cell:hover,
.game-tile:hover { background: var(--ink-soft); }

.game-cell:focus-visible,
.game-tile:focus-visible,
.game-btn:focus-visible {
  outline: var(--stroke) solid var(--ink-edge);
  outline-offset: 3px;
}

/* A piece in your hand is LIFTED — the hard offset shadow is the world's own
   "this one is off the sheet" device (DESIGN.md material rules). */
.game-tile[aria-pressed="true"] {
  background: var(--ink);
  color: var(--stock);
  border-color: var(--ink);
  box-shadow: var(--shadow-lift);
  transform: translate(-2px, -2px);
}

/* Printed on the sheet rather than dealt to the player: it keeps the ruling
   showing through, so a fixed cell reads as part of the armature. */
.game-cell[data-fixed="yes"] {
  background:
    repeating-linear-gradient(-45deg,
      var(--sheet-rule) 0 1px, transparent 1px 6px),
    var(--stock-deep);
  cursor: default;
}
/* There is deliberately NO `color: transparent` on an empty cell. The first
   cut had one, and it resolved `currentColor` to nothing inside any SVG a
   game draws in an empty cell: Same Name's signs were invisible until the
   cell was solved, in a game about looking at signs. Fifteen writes '' into
   an empty cell, so the rule was never load-bearing; it only hid other
   games' artwork. Blank a cell by writing nothing into it. */

.game-tray {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 1rem;
  padding: .6rem;
  border: 1px solid var(--sheet-rule-strong);
  min-height: 62px;
  align-items: center;
}
.game-tray:empty::before {
  content: "empty";
  font-weight: 700;
  opacity: .6;
}

/* -------------------------------------------------------------- quantity -- */

/* AMOUNT IS LENGTH, NOT A NUMERAL.
   "Cloud 0" is a dashboard reading; a row of cells that fills is the world.
   A child sees how much there is without decoding a digit, which is the whole
   argument for the grid: the armature already gives them a ruler. */
.game-meter {
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 0;
  margin: 0;
  list-style: none;
}
.game-meter-cell {
  width: 14px;
  height: 22px;
  border: 1px solid var(--sheet-rule-strong);
  background: var(--stock);
}
.game-meter-cell[data-on="yes"] {
  background: var(--ink);
  border-color: var(--ink);
}
/* The newest cell carries the diagonal, so "what just changed" is visible
   without colour and without motion. */
.game-meter-cell[data-new="yes"] {
  background:
    linear-gradient(to top right,
      var(--ink) calc(50% - 1px), var(--stock) calc(50% - 1px),
      var(--stock) calc(50% + 1px), var(--ink) calc(50% + 1px));
}

/* --------------------------------------------------------------- readout -- */

.game-readout {
  font-family: var(--font-display);
  font-size: .95rem;
  color: var(--board);
}
.game-readout[data-state="ok"] { color: var(--ink-edge); }
.game-readout[data-state="ok"]::after { content: "\00a0\2713"; }
.game-readout[data-state="no"]::after { content: "\00a0\00d7"; }

/* -------------------------------------------------------------- controls -- */

.game-controls {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem;
  margin: 1rem 0 0;
}

.game-btn {
  font: inherit;
  font-family: var(--font-display);
  font-size: .9rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  min-height: var(--cell);
  min-width: var(--cell);
  padding: .5rem 1.05rem;
  color: var(--board);
  background: var(--stock);
  border: var(--stroke) solid var(--rule);
  border-radius: var(--die-cut);
  box-shadow: var(--shadow);
  cursor: pointer;
}
.game-btn:hover { background: var(--ink); color: var(--stock); }
.game-btn:active { transform: translate(2px, 2px); box-shadow: none; }
.game-btn[disabled] {
  cursor: default;
  box-shadow: none;
  color: var(--ink-edge);
  /* Not colour alone: a disabled control loses its shadow, so it reads as
     flat on the sheet rather than pressable, and takes the armature's own
     hatching so it looks printed rather than dealt. */
  background:
    repeating-linear-gradient(-45deg,
      var(--sheet-rule) 0 1px, transparent 1px 6px),
    var(--stock-deep);
}
.game-btn[data-primary="yes"] {
  background: var(--ink);
  color: var(--stock);
  border-color: var(--ink);
}

/* ---------------------------------------------------------------- status -- */

/* Printed along the foot of the sheet like the caption on a box back.
   ALWAYS VISIBLE, never screen-reader-only: the announcement and the sentence
   a sighted child reads are the same string, because two texts for one fact
   is two things to keep in agreement and one of them goes stale. */
.game-status {
  margin: 1.1rem 0 0;
  padding: .8rem 1rem;
  min-height: 3.2em;
  font-weight: 700;
  background: var(--ink-soft);
  border: 1px solid var(--sheet-rule-strong);
  border-radius: var(--die-cut);
}

.game-note {
  margin: .6rem 0 0;
  font-size: .9rem;
  color: var(--board);
  opacity: .85;
}

/* ------------------------------------------------------------ the section -- */

.games-board { margin: 0; }

.games-board .board-region {
  margin: 0 0 2.2rem;
  padding: 1.1rem 1.2rem 1.4rem;
  border: var(--stroke) solid var(--rule);
  border-radius: var(--die-cut);
  box-shadow: var(--shadow);
  background: var(--stock);
  background-image:
    repeating-linear-gradient(to right,
      color-mix(in srgb, var(--ink) 16%, transparent) 0 1px, transparent 1px 48px),
    repeating-linear-gradient(to bottom,
      color-mix(in srgb, var(--ink) 16%, transparent) 0 1px, transparent 1px 48px);
}
.games-board .board-region h2 {
  margin: 0 0 .9rem;
  color: var(--ink);
}

/* A LONE ENTRY TAKES THE WHOLE ROW.
   The shared article-list is a `repeat(auto-fill, minmax(17rem, 1fr))` grid,
   which is right for a category page holding sixteen articles and wrong here:
   with one game per subject it left a small card marooned in a field of empty
   ruling, which reads as unfinished rather than spacious. A specimen sheet
   lists its entries as full-width ruled rows, and that is also what scales —
   a second game in a subject simply sits beside the first. */
.games-board .article-list > li:only-child { grid-column: 1 / -1; }
.games-board .article-list > li:only-child .summary { max-width: 52ch; }
.games-board .empty {
  margin: 0;
  font-weight: 700;
  opacity: .75;
}

/* "See all 7 Science games" -- shown only when a region holds more than the
   board previews. It is a way OUT of the board, so it is set apart from the
   cards by the region's own ruling rather than dressed up as a seventh card:
   a link that looks like a game is a link a child presses expecting a game. */
.games-board .region-more {
  margin: 1rem 0 0;
  padding-top: .75rem;
  border-top: 1px solid var(--sheet-rule-strong);
  font-weight: 700;
}

.game-page .game-teaches {
  font-size: 1.15rem;
  font-weight: 700;
  max-width: var(--measure);
}
.game-page .game-meta { font-size: .92rem; }

.card-kind {
  display: inline-block;
  margin-left: .5rem;
  padding: .1rem .45rem;
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--stock);
  background: var(--ink);
  border-radius: var(--die-cut);
}

/* ------------------------------------------------------------------ a11y -- */

@media (prefers-reduced-motion: reduce) {
  /* State still CHANGES; it simply arrives without travel (DESIGN.md motion).
     Nothing here hides a change — that would be worse than animating it. */
  .game-cell,
  .game-tile,
  .game-btn,
  .game-meter-cell,
  .game-board * {
    transition: none !important;
    animation: none !important;
  }
  .game-tile[aria-pressed="true"],
  .game-btn:active { transform: none; }
}

@media (prefers-contrast: more) {
  .game-mount { border-width: 4px; background-image: none; }
  .games-board .board-region { border-width: 4px; background-image: none; }
  .game-status { background: var(--stock); border-width: 2px; }
  .game-btn[disabled] { background: var(--stock-deep); text-decoration: line-through; }
  .game-stagebar li,
  .game-board,
  .game-tray,
  .game-meter-cell { border-color: var(--rule); }
}

@media (max-width: 26rem) {
  /* On the narrowest phones the controls become full-width rows: a 48px
     target a thumb misses is a 48px target that failed. Games laying out
     their own control cluster (Mirror Wing's cursor pad) opt out by setting
     their own display on the container. */
  .game-controls { flex-direction: column; align-items: stretch; }
  .game-btn { width: 100%; }
  .game-mount { margin-inline: -0.6rem; width: calc(100% + 1.2rem); }
}
