/* =====================================================
   THE GAME SCREEN IS ONE SCREEN
   Phase 3. css/kid-games.css lays a game out as a DOCUMENT — a column that
   grows to whatever is in it — and says so in as many words: "a flex item
   only grows into free space, so a page with none ... a mounted game taller
   than the viewport ... is left exactly as it was." Measured at 390x844,
   seven of the eight games were exactly that:

     story-order 2112px   star-map 1231   word-trek 1150   stage-lights 972
     colour-creatures 1066   letter-trace 874   puppy-count 849

   THE COST IS NOT THE SCROLLING, IT IS WHAT THE SCROLLING SEPARATES. A game
   asks a question at the top of the column and puts the answers at the
   bottom of it. At 2112px story-order could not show the picture she was
   placing and the slot she was placing it into at the same time: she scrolled
   down to find the tray, and the row of slots — the thing that says what the
   move even is — left the screen while she chose. Every game here is built on
   a spoken prompt and a visible target, and a screen that cannot hold both at
   once has broken the pairing the whole design rests on.

   SO THE FRAME IS FIXED AND THE CONTENT FITS INSIDE IT. While a game is
   mounted the document is exactly one viewport tall. The header, the jar, the
   companion row, the prompt card and the toolbar keep their natural heights
   and never move. Exactly one region per game — the one marked .kg-fit — is
   given whatever height is left and lays itself out inside that.

   WHY ONE REGION AND NOT A GLOBAL SHRINK. Most of what is on a game screen
   must not shrink: .kg-target has a floor (57px for Abby, 76px for Bella)
   that is a brief requirement, not a preference, and the prompt card is the
   sentence being spoken. The only thing that can honestly absorb a smaller
   screen is the play area itself, so that is the only thing asked to.

   NOTHING IS EVER TRAPPED. This sets `height`, not `overflow: hidden`. A
   screen too small for even the fitted layout — landscape, a large text-zoom
   setting — overflows the body and scrolls exactly as it does today. The
   fitted layout is the good case, not a cage.
   ===================================================== */


/* ---------------- The frame ----------------
   js/kidgames/host.js adds .kg-playing to <body> in showGame() and takes it
   off in showHub(), beside the meterCompact() call that already marks the
   same moment. The hub keeps the growing column it was designed around —
   kid-hub.css centres .kg-main in the leftover space and that only works
   while there IS leftover space. */

/* `height`, where .storybody sets `min-height`. A definite height is the
   whole mechanism: a flex child cannot be asked to shrink inside a parent
   that is still sizing itself to its contents. */
.kg-playing { height: 100dvh; }

/* min-height: 0 on every link in the chain. Without it a flex item's
   automatic minimum size is its CONTENT size, and the tray simply pushes the
   column open again — the single most common way a layout like this silently
   does nothing. */
.kg-playing .kg-main {
  min-height: 0;
  /* kid-hub.css centres this column for the hub. On a game screen the mount
     takes the slack instead, so there is nothing left to centre and packing
     from the top is what keeps the prompt card in the same place round to
     round. */
  justify-content: flex-start;
}

/* THE LINK THAT IS EASY TO MISS. The two screens share one grid cell so
   motion.js's transitionScreens() can overlap them (css/kid-hub.css's
   .kg-stage-shell), and that grid sits between .kg-main and the mount. Left
   alone it is `flex: 0 1 auto` with `min-height: auto` and `align-items:
   start` — it neither takes the slack nor lets its cell shrink, so every rule
   below it was correct and did nothing. Measured: main bounded at 776px, the
   stage 941px inside it.

   `start` is right for the hub, where the scene should sit at its own height
   at the top of the cell. On a game screen the mount IS the cell. */
.kg-playing .kg-stage-shell {
  flex: 1 1 auto;
  min-height: 0;
  grid-template-rows: minmax(0, 1fr);
  align-items: stretch;
}
/* A grid item's automatic minimum size is its content, exactly as a flex
   item's is. The sheet already zeroes min-width here for the same reason in
   the other axis. */
.kg-playing .kg-stage-shell > * { min-height: 0; }

.kg-playing .kg-mount {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.kg-playing .kg-game-root {
  flex: 1 1 auto;
  min-height: 0;
}

/* ONLY THE ELASTIC REGION IS ELASTIC. A flex item's default is
   `flex-shrink: 1`, so the moment the column is bounded EVERY band becomes
   negotiable — and flexbox took the height out of the text first. word-trek
   opened with "Welcome to the trailhead. Tap a word, then tap the pack it
   belongs in." squashed into a 28px box with the second line spilling behind
   the head card, which paints an opaque white background over it. It read as
   a truncated sentence, and it was on screen for the whole first round.

   The prompt is the spoken line printed. Nothing about it is slack, and the
   same goes for the legends, the head and the toolbar. They keep their
   natural height; .kg-fit below re-declares its own shrink because it is the
   one thing here that is allowed to give. */
.kg-playing .kg-game-root > * { flex-shrink: 0; }


/* ---------------- The one region that absorbs it ----------------
   A game marks its play area .kg-fit — the canvas, the star field, the pad
   grid, the picture tray. One class, one line per game, the same shape as the
   companion's per-game scaffold call: the game says which of its own regions
   is the elastic one, because only the game knows. */
.kg-playing .kg-fit {
  /* Re-declared after the shrink lock above: this is the region that gives. */
  flex: 1 1 auto;

  /* NOT min-height: 0, WHICH IS THE TRAP THIS RULE EXISTS TO AVOID.
     Zeroing the minimum is what lets the chain above shrink at all, so the
     obvious thing is to zero it here too — and then the elastic region absorbs
     every last pixel of overflow and the page "fits". Measured with min-height
     0: the word-trek tray came out 8px tall and story-order's 18px. Both games
     passed a document-height check while being impossible to play.

     A play area is not slack. The floor is one row of targets at the size the
     brief requires, plus the slop they are tapped by: below that there is
     nothing a move could be made in, and a page that overflows is a far
     smaller failure than a game that cannot be played. Games whose pieces are
     bigger than a target — story-order places whole illustrations — raise it
     in their own sheet. */
  min-height: calc(var(--kg-target, 57px) + 2 * var(--kg-slop, 8px) + 8px);
}

/* Regions that CAN scale — an <svg> with a viewBox — do so on their own once
   the box is bounded, and never reach the overflow rule below. */
.kg-playing .kg-fit > svg { width: 100%; height: 100%; }

/* Regions that CANNOT scale — a grid of targets at their floor — scroll
   inside the frame instead. This is the fallback, and it is still the point:
   the prompt, the companion and the toolbar stay on screen while she scrolls
   the choices, which is exactly what scrolling the page took away.
   overflow-y, not overflow: a horizontal scrollbar here would be the layout
   failing sideways, and should be visible as a bug rather than papered over. */
.kg-playing .kg-fit-scroll {
  overflow-y: auto;
  /* The targets carry --kg-slop as padding and a focus ring outside that. A
     scroll container clips both against its edge, so it is given the room
     back. */
  padding: 4px var(--kg-slop, 8px);
  /* Momentum scrolling stops at this box rather than dragging the page
     underneath it, which on iOS is what makes a nested scroller feel like a
     nested scroller instead of a stuck page. */
  overscroll-behavior: contain;
}


/* ---------------- Where the last hundred pixels come from ----------------
   With the frame in place six of the eight games fit outright. The two that
   did not are the two that put a SOURCE and a DESTINATION on screen at once —
   story-order's pictures and slots, word-trek's words and packs — because
   both regions have to be visible to make a single move, and neither can be
   the elastic one.

   THE GAP WAS BIGGER THAN THE TRAY. .kg-game-root stacks eight bands with a
   16px gap: 112px of air, against a picture tray the floor above was holding
   open at 101px. On a screen with room that rhythm is right. On a phone it is
   spending the play area on the spaces between things.

   THIS IS NOT THE 30px RULE. Brief §4's floor is the gap BETWEEN ADJACENT
   TARGETS, which lives on .kg-choices and is not touched here or anywhere in
   this file. This is the gap between bands of the page. */
@media (max-height: 900px) {
  .kg-playing .kg-game-root { gap: 10px; }

  /* The legend over each band names it in two words with a mark beside it.
     Two of them cost 48px in the two games that have the least to spare. */
  .kg-playing .kg-game-root .kg-story-order-legend,
  .kg-playing .kg-game-root .kg-word-trek-legend {
    margin-top: 0;
    margin-bottom: 0;
    font-size: .95rem;
  }

  /* The spoken prompt, printed. It stays at a size a new reader can read —
     this only takes back the leading and the padding around it, which is why
     the floor is on line-height rather than font-size. */
  .kg-playing .kg-game-root .game-msg {
    line-height: 1.3;
    margin-top: 0;
    margin-bottom: 0;
  }

  /* word-trek's head is a card of four stacked lines and was 184px — the
     single largest thing on the screen after the two play areas. NOTHING HERE
     HIDES A LINE. The clue is the definition of the word she is holding, which
     is the reading this game is actually for, and the pips are her progress
     through the pack. What comes back is the air: the card's padding and the
     margins stacked between four short paragraphs. */
  .kg-playing .kg-word-trek-head { padding: 9px 14px 8px; }
  .kg-playing .kg-word-trek-task { margin-bottom: 2px; }
  .kg-playing .kg-word-trek-pips { margin-top: 4px; }
  .kg-playing .kg-word-trek-clue { margin-top: 5px; line-height: 1.32; }

  /* The game's own name, which the header does not carry — it says whose
     games these are, not which one is open — and which host.js focuses when
     the swap lands. Smaller, never gone. */
  .kg-playing .kg-game-root > h2 { font-size: 1.1rem; margin: 0; }

  /* THE DESTINATION BOXES, AT THE FLOOR THEY DOCUMENT RATHER THAN ABOVE IT.
     These are the two games that must show a source and a destination at
     once, and in their biggest rounds the destination is a grid two rows
     deep — six slots for story-order, four packs for word-trek — so every
     pixel here is paid twice.

     Both sheets say what the real floor is. story-order.css: "72px is still a
     target and a half: Brief §4 is 57 for Abby." These take that headroom and
     nothing else: 64px and 92px are still above 57, both are min-heights that
     grow with their contents, and neither is touched on a screen with room.

     WHAT IS NOT TOUCHED: word-trek's clue panel reserves 5.2em because 25 of
     its 32 definitions run to three lines, and a panel that grew when she
     asked what a word means would shove the tray down as she reached for it —
     the exact bug #8 fixed by making the slot strip sticky. A reserve is not
     padding. It stays. */
  .kg-playing .kg-story-order-slot { min-height: 64px; }
  .kg-playing .kg-word-trek-bin { min-height: 92px; }

  /* THE COMPANION GOES TO ITS FLOOR, NOT BELOW IT. Phase 2 sizes the figure
     clamp(var(--kg-target), 19vw, 88px) — 74px on a 390px screen. Pinning it
     to the clamp's own lower bound gives the play area 17px and keeps the
     figure exactly at the tap-target floor, because tapping it is how a child
     replays the prompt. This is the one piece of chrome on a game screen that
     can give anything back: the 28px above the mount is brief §4's clearance
     before the way out, and the 56px toolbar button and the 30px between
     targets are the same rule again. */
  .kg-playing { --kg-cmp-size: var(--kg-target, 57px); }
}

/* ---------------- The win banner is not part of the game ----------------
   host.js renders .kg-win INSIDE the game's own root, deliberately — a local
   banner rather than the global celebrate() that would take the screen away.
   Inside a fixed frame that means it has to share the height with a play area
   that no longer has anything to play. host.js marks the root .kg-won, and
   the elastic region gives its height up: the banner is the screen now. */
.kg-playing .kg-game-root.kg-won .kg-fit {
  flex: 0 1 auto;
  /* The floor exists to keep a play area PLAYABLE. A won game is not being
     played, so the floor stops applying — the region keeps whatever room is
     left over and gives the rest to the banner, which is how the finished
     board stays visible behind the reward instead of being hidden by it. */
  min-height: 0;
}

/* Releasing the play area alone was not enough, and the measurement said so:
   with the banner up, star-map ran to 1013px on an 844px screen and both of
   the banner's buttons — "Play again" and "Back to the map" — sat below the
   fold at 872 and 941. A reward she has to go looking for is not one.

   These two bands are the ones a win makes redundant. The prompt card is the
   question, and it has just been answered; the toolbar is the 🔊 that repeats
   it. Neither is removed from the DOM — the companion's repeat still finds
   .kg-replay by class, and a display:none button is still there to find. */
.kg-playing .kg-game-root.kg-won > .game-msg,
.kg-playing .kg-game-root.kg-won > .game-toolbar {
  display: none;
}

/* The last resort, and it must exist. A win banner is the one thing on these
   pages whose height is content the game chooses — a long title on a narrow
   screen — so if it still does not fit, it scrolls INSIDE the frame rather
   than becoming unreachable. */
.kg-playing .kg-game-root.kg-won {
  overflow-y: auto;
  overscroll-behavior: contain;
}
