
/* ==========================================================================
   2. THE SPINE — board and dock. Two layouts and layout() picks between
   them on ONE test (is there room for a flank?), never on taste.
   ========================================================================== */
/* ROUND 3, TASK 7: ONE CONTAINER, STRETCHED. The floating round centred a
   pane-plus-controls pair as a block and let the sky own the rest; the owner
   looked at it and named the real problem ("the UI looks so poorly organized,
   get inspired by Plinko and bird"). Both references share one grammar: ONE
   bordered surface holds the whole game, the board is the hero inside it, and
   the bet controls are a single tight strip of that same surface - Plinko's a
   ~300px column on the flank, Bird's a band along the floor. So #stage is one
   stretched cell and #board - the container - fills it; the sky still paints
   the frame behind it and reads through the glass. */
#stage{position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);
  padding:var(--band,62px) var(--pad) var(--pad)}

/* TASK (r2) 5: THE STAGE AND THE BET UI GET THEIR OWN CONTAINERS. The
   owner's words: "I also like there is a container for the game and a
   different one for the bet UI." Measured against what was actually
   shipping: #stage — the GRID, spanning both the board column and the dock
   column — carried the radial ground and the starfield, so both bled under
   the dock as much as under the board and neither object read as bounded.
   #dock (in the flank) never had a panel of its own either — see 70-dock.css's
   own "FRAMELESS IN THE FLANK" ruling, which answered a different complaint
   (round 1's dock wore the FRAME's own window treatment one level too deep)
   and is not reopened here. The fix moves the ground and the stars off
   #stage — which stays a pure grid, no ground of its own — and onto #board,
   which is now a bounded panel exactly the way v1's `.stage` is: its own
   border, its own radius, its own ground. #dock keeps ITS existing panel
   language (bordered card in the stack, sized-to-content card centred on a
   scrim in the flank — 70-dock.css, untouched) rather than gaining a new
   one, which is what "keep the dock's own panel distinct from it" asks for:
   two different treatments reading as two different objects, not one
   treatment copied onto both. */
/* THE CONTAINER IS SINGULAR AND IT IS #frame — the owner, this round: "no
   multiple containers a single one with the background". The glass card that
   stood here (border, radius, lit top edge, its own wash) was a second
   container inside the bordered frame, and it is DELETED, not dimmed: #frame
   already carries the one border, the one radius, the radial ground and the
   starfield (05-frame.css), so #board is a pure layout region now — no
   border, no background, no shadow of its own. overflow:hidden survives
   because the travelling chip is clamped to this box (§14 of the check) and
   the clip is the backstop the clamp is measured against.
   ONE ARRANGEMENT, the owner's candidate A: the game above, the console
   beneath, one centred column at every width. The side-column lay
   (data-lay="side", the left dock with its darker wash and hairline) is
   retired with the ruling that chose A; the two console DENSITIES that
   remain are 70-dock.css's business. */
/* THE CLUSTER IS ONE OBJECT AND THE SKY TAKES THE SLACK — the owner, at
   2516x1261: "we should always center the game and UI". The 1fr-game-row
   arrangement this replaces pinned the console to the frame's foot and let
   the game region keep every spare pixel, so at a big monitor the modes,
   the track and the console drifted hundreds of px apart (measured: 263px
   between modes and track at 2560x1261, against 22.8 at 1280x760) — three
   strata, not a game. Now the playable unit — modes, track, readouts,
   console — keeps its own fixed rhythm (--pgap/--ppadv, per data-room,
   never per frame height) and the two minmax(0,1fr) SKY rows above and
   below it split ALL the slack equally: the cluster floats centred in the
   region at every size. minmax(0,*) rather than align-content:center so a
   frame too small for the cluster collapses the sky to zero and overflows
   the way the demotion pass has always measured it, instead of leaking out
   of both ends of a centred box. */
/* THE UNIT CENTRES — THE OWNER'S RULING, NOW TWICE. Q1's bottom anchor
   (one sky row above, the cluster landed on a horizon at the frame's
   foot) went to the owner's real screen and came back with its verdict
   verbatim: "How is this good? I'm literally playing on bottom 20%
   screen." / "We can have centered with the UI organized in a
   container." So the two-sky-row centring returns AND KEEPS the fix the
   bottom anchor was made for: the cluster — modes, track, readouts,
   console card — is still one block with a fixed internal rhythm
   (--pgap/--skygap, layout()-written, never a function of frame height),
   and the two minmax(0,1fr) sky rows split ALL the slack equally — sky
   above AND below, at every size. minmax(0,*) rather than
   align-content:center so a frame too small for the cluster collapses
   both skies to zero and overflows the way the demotion pass has always
   measured it. The horizon, its ground layer and the star stop-line are
   retired with the anchor (their rules below are deleted, not dimmed) —
   the console's ground is its own card now, 70-dock.css. */
/* THE CLUSTER'S ROW ORDER IS THE OWNER'S (2026-08-27): history — track
   region — mode rail — console, still one block between the two sky rows.
   The rail's row ("move the under, over selector to beneath the game
   above the betting UI") is the BOARD's, never the console card's: the
   rendered in-dock variant cost the chip lane 25px at 380x300 and
   layout() deleted the WIN bubble — the rail's height re-enters the same
   budget it left, which is what keeps the bubble alive (asserted). */
#board{position:relative;overflow:hidden;width:100%;height:100%;
  display:grid;
  grid-template-rows:minmax(0,1fr) auto auto auto auto minmax(0,1fr);
  grid-template-areas:"." "hist" "game" "modes" "dock" "."}
/* the sky is a FRAME child now (see index.html) — same rule, one level up,
   and z-index:0 keeps it under #stage's own stacking context */
/* the sky is FULL-BLEED again — the horizon it used to stop at is retired
   with the bottom anchor (see #board above): the unit floats centred in
   open sky, stars above and below it, and the console's own card is what
   separates the money surface from the sky now */
.stars{position:absolute;inset:0;
  overflow:hidden;pointer-events:none;opacity:.55;z-index:0}
.stars i{position:absolute;left:0;top:0;display:block;background:transparent}
.stars i::after{content:"";position:absolute;top:2000px;left:0;
  width:inherit;height:inherit;box-shadow:inherit;border-radius:inherit}
.st1{width:2px;height:2px;border-radius:50%;animation:animStar 60s linear infinite}
.st2{width:3.5px;height:3.5px;border-radius:50%;animation:animStar 120s linear infinite}
@keyframes animStar{from{transform:translateY(0)}to{transform:translateY(-2000px)}}

/* ==========================================================================
   THE ROOM CELEBRATES — win round (2026-08-27), W4 merged with the T4 seam
   RULED. Light rises through the tiers and REVERSES at the top:
     tier 1  the room does nothing (Plinko's rung-0 rule — a ~1x arrives on
             half of all rounds and must not move the room it lands in)
     tier 2  the light changes hands, contained: warm crossfades out, the
             gold #roomlit rises at the landing, .66 / scale 1.10
     tier 3  it FLOODS (opacity 1 / 1.30) and the sky answers — #roomsky's
             70 gold twins catch the light
     tier 4  the room goes OUT — W4's flood does not escalate again; W5's
             #roomdim darkness takes the frame and the strike, the lit
             ruler residue (.40/.62) and the WON figure carry it. This is
             the ruling on the preview's known-bad >=50x combo (W4 never
             read data-mag 4; the dim covered a still-red warm layer).
   ---- 1. the warm light: 05-frame.css's old FIRST background layer, on
   its own element so it can CROSSFADE (never stack — the stacked version
   renders as sepia mud; W4's rendered proof). Sits BEFORE .stars in the
   DOM: the frame's background painted under the sky, and the idle/loss
   frame stays pixel-identical to the shipped stacking. */
#roomwarm{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:1;
  background:radial-gradient(62% 46% at var(--cx,50%) var(--cy,44%),
    rgba(192,24,12,.17) 0%, rgba(192,24,12,.055) 44%, rgba(192,24,12,0) 72%);
  transition:opacity .42s linear}
/* ---- 2. the gold light, born at the LANDING. The gradient is painted once
   at the layer's own centre and the LAYER is translated onto --wxpx
   (settle()'s one write per win, off setMk's own formula), so moving the
   emitter is a composited transform, never a full-frame repaint. What the
   shift uncovers is transparent — the physically right answer: a light by
   one wall does not reach past the other. */
#roomlit{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:0;
  transform-origin:50% var(--cy,44%);
  transform:translateX(calc(var(--wxpx,50%) - 50%)) scale(.86);
  background:radial-gradient(54% 40% at 50% var(--cy,44%),
    rgba(243,195,63,.34) 0%, rgba(243,195,63,.10) 30%, rgba(243,195,63,0) 60%);
  transition:opacity .42s linear, transform .42s linear}
/* ---- 3. the sky's own stars catch it: same seed, same LCG, same call
   order as the shipped starfield, every 6th entry — 70 of the sky's 420
   stars, at their own coordinates, in gold (built by the starfield IIFE,
   index.html). */
#roomsky{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:0;opacity:0;
  transform-origin:var(--wxpx,50%) var(--cy,44%);transform:scale(.945);
  transition:opacity .42s linear, transform .42s linear}
#roomsky i{position:absolute;left:0;top:0;display:block;background:transparent;
  width:3.5px;height:3.5px;border-radius:50%;
  animation:animStar 60s linear infinite}
#roomsky i::after{content:"";position:absolute;top:2000px;left:0;
  width:inherit;height:inherit;box-shadow:inherit;border-radius:inherit}
/* ---- 4. the darkness — W5's #dim, a SUBTRACTION, over the sky and the
   room layers and never over a control (#stage paints after it). */
#roomdim{position:absolute;inset:0;pointer-events:none;z-index:0;opacity:0;
  background:#05060A;transition:opacity .42s ease-out}
/* ---- 5. the ladder is REACH, and it reverses at the top */
#frame[data-res="win"][data-mag="2"] #roomwarm,
#frame[data-res="win"][data-mag="3"] #roomwarm,
#frame[data-res="win"][data-mag="4"] #roomwarm{opacity:0;
  transition:opacity calc(.24s * var(--celk,1)) linear}
#frame[data-res="win"][data-mag="2"] #roomlit{opacity:.66;
  transform:translateX(calc(var(--wxpx,50%) - 50%)) scale(1.10)}
#frame[data-res="win"][data-mag="3"] #roomlit{opacity:1;
  transform:translateX(calc(var(--wxpx,50%) - 50%)) scale(1.30)}
#frame[data-res="win"][data-mag="2"] #roomlit,
#frame[data-res="win"][data-mag="3"] #roomlit{
  transition:opacity calc(.30s * var(--celk,1)) ease-out,
             transform calc(.44s * var(--celk,1)) cubic-bezier(.2,.72,.3,1)}
#frame[data-res="win"][data-mag="3"] #roomsky{opacity:.85;transform:scale(1);
  transition:opacity calc(.32s * var(--celk,1)) ease-out calc(.10s * var(--celk,1)),
             transform calc(.64s * var(--celk,1)) cubic-bezier(.15,.75,.3,1) calc(.10s * var(--celk,1))}
#frame[data-res="win"][data-mag="4"] #roomdim{opacity:.72;
  transition:opacity calc(.34s * var(--celk,1)) ease-out}
/* ---- 6. the leave is the house's celebration-yield clock */
#frame[data-resfade] #roomwarm{opacity:1;transition:opacity .42s linear}
#frame[data-resfade] #roomlit,
#frame[data-resfade] #roomsky,
#frame[data-resfade] #roomdim{opacity:0;transition:opacity .42s linear}

/* ---- THE GROUND IS DELETED, NOT DIMMED ----------------------------------
   #hzGround (the full-bleed horizon at the frame's foot, its border-top
   stroke, graft B1's catch-light and the full-room warm lift) left with
   the bottom anchor — the owner's centring ruling removes the frame-foot
   line the ground stood on, and its two jobs moved: separating the money
   surface from the sky is the console CARD's (70-dock.css), and the
   machined chamfer went with it onto the card's own top edge. */

/* THE PAD IS SPENT ONCE PER AXIS, which is Mines' rule and it is here for the
   same reason: #stage pads horizontally, #board pads vertically, so the board's
   content box is exactly `w - dockw - 2*pad` by `h - band - pad` and nothing is
   double-counted. 68 = the corner's bottom edge (18+42) plus 8px of air —
   the wf-ui chromeClear rebudget; layout()'s --band comment is the story. */
/* THE BOARD IS ONE CENTRED COLUMN ON ONE MEASURE, and that is a correction
   made from a screenshot rather than from the geometry. The first cut gave the
   well a 1fr row and let the other four rows sit wherever the grid put them:
   every assertion passed - nothing overlapped, nothing overflowed - and the
   picture was four unrelated objects scattered down 780px of black, with the
   mode rail 300px above the track it repaints and the figures pinned to the
   floor. A collision check cannot see that.
   So the play surface is a GROUP: modes, track and figures share one width
   (--playw) and one vertical stack, centred in the board (history rode this
   group too until the owner retired the board strip — see index.html at
   #play). Everything on
   it is measured against the same two verticals, which is the only reason a
   composition reads as designed. */
/* THE PLAY GROUP CENTRES IN THE GAME REGION on one definite measure. The
   place-content:center lesson from earlier in the round still holds (an auto
   track sizes to max-content and a percentage width resolves against it, so
   deleting a readout cell once shrank the rail 190px) - the width is min(playw,
   region minus breathing room), stated, never derived from content. */
#play{position:relative;z-index:1;grid-area:game;place-self:center;
  width:min(var(--playw,760px),100% - 2*var(--bpad,22px));display:grid;
  grid-template-rows:auto auto auto;gap:var(--pgap,20px);
  /* THE FULL-ROOM STRETCH IS DELETED, AND ITS OWN REASONING IS WHY. It
     answered candidate A's quiet upper half at 1280x760 by stretching the
     group and giving the track's row the slack — which at 1280x760 was
     ~1px of drift and at the owner's 2516x1261 monitor was 263px between
     the mode rail and the track it repaints: the "deliberate" anchoring
     scaled with the frame and became three strata in a sky. The ruling
     that replaces it lives on #board above: the group keeps ALL-auto rows
     at every room, so its internal gaps are exactly --pgap and never a
     function of frame height, and the cluster (this group + the console)
     centres as one block between #board's two sky rows. */
  /* --ppadv, not --bpad: at the docked minimum the vertical half of the
     breathing room is the difference between the group fitting its region
     and the result figure poking out through the container top - measured,
     21px over at 380x300 with the dock now inside the card. layout() writes
     0px there (2 until R1 item 5's rung repayment) and bpad everywhere
     else, and budgets with the same number. */
  /* WF-UI: the BOTTOM half of the padding is deleted with the bottom sky
     row — the group's standoff from the horizon is --skygap now (28/18/2,
     layout()-written, counted in the same budget), a stated gap to a
     stated line rather than symmetric breathing room in a centred box. */
  /* margin-bottom is --pgap now, not --skygap: the rail row sits directly
     beneath the track region (the owner's row order) and the sky gap moved
     onto the rail's own margin to the console card below */
  padding:var(--ppadv,22px) 0 0;margin-bottom:var(--pgap,20px);min-height:0}
/* (no #well rule needed: 40-track.css's align-self:center on it is inert in
   an auto row — kept there because it is the track's own file's business) */

/* THE LAST-ROLLS ROW — the cluster's top line, on the play group's own
   measure (the formula copied, not paraphrased — the one-measure rule).
   THE LANE IS RESERVED FROM BOOT: min-height holds the pill row's box
   while it is empty, so the first settle cannot shift the centred cluster
   — the layout half of the entrance defect that got the old strip removed
   ("random writting tab appearing"; the pill half is the hin entrance in
   30-history.css). Empty it PAINTS nothing: no background, no caption, no
   furniture — the reserved sky is indistinguishable from the sky around
   it, which is what the no-orphaned-glyph rule asks of an empty record. */
/* FLUSH RIGHT, LIKE THE PRECEDENT IT CITES — casino-manager, this round:
   the first cut centred the row, so for the session's first six rolls the
   newest pill was a moving target while the row grew symmetric. BirdCrash's
   ticker (games/BirdCrash/index.html: .tick__track{justify-content:
   flex-end}) pins the line to the right so the newest pill's position
   never moves — the glance a multi-tabling player actually makes. With
   overflow:hidden this also clips the OLDEST end first if the row ever
   outgrows the measure, which is the correct end to lose. */
#hist{position:relative;z-index:1;grid-area:hist;justify-self:center;
  width:min(var(--playw,760px),100% - 2*var(--bpad,22px));
  min-height:calc(24px*var(--scc,1));margin-bottom:var(--pgap,20px);
  display:flex;justify-content:flex-end;align-items:center;
  gap:calc(6px*var(--scc,1));min-width:0;overflow:hidden}

/* THE MODE RAIL'S ROW — beneath the game, above the console card (the
   owner's placement), on the same measure as everything else in the
   cluster. The control itself is 80-auto.css's, untouched; this is only
   its position in the spine. */
#modes{grid-area:modes;position:relative;z-index:1;justify-self:center;
  width:min(var(--playw,760px),100% - 2*var(--bpad,22px));
  margin-bottom:var(--skygap,28px)}

/* ==========================================================================
   PORTRAIT — THE WATERLINE (mobile round, 2026-08-27; layout()'s data-port
   is the gate, its own comment the reasoning). One line draws it: #board's
   BOTTOM sky row goes to 0, so every spare pixel is sky ABOVE the game and
   there is none under the console. Below the line the finger works and
   nothing is under 44px; above it the eye works, and the only touchable
   things are the two figures that price the bet, which cross the line on
   purpose. */
#frame[data-port] #board{grid-template-rows:minmax(0,1fr) auto auto auto auto 0}
/* THE SLACK GETS A SECOND SEAT, INSIDE THE CLUSTER'S OWN JOINT (mobile
   polish, 2026-08-28 — the owner, from a live phone: "We could use the
   space better"). The waterline put ALL slack above the game — right
   against dead-ground-under-ROLL, but the baseline render showed the cost:
   129-172px of empty sky over a cluster whose mode rail sat 12px off the
   console card while the top third held nothing. The phone rung now splits
   the slack 1 : .25 across the sky and the rail->console joint — the game
   block (history, track, rail) floats up toward the room's centre, the
   console stays the thumb bar, and the WATERLINE ITSELF HOLDS: the last
   row is still 0, nothing sits under the CTA, and §27a's phone leg is
   green verbatim. The tablet's .6fr split below is the precedent grammar
   (centred-low, thumb zone kept); this is its phone-scaled cousin at the
   OTHER joint. [data-narrow] scopes it to phones: the 7-row template must
   not reach the tablet rule, whose 6 row values would put the dock in the
   joint's own row. Short phones have no slack — both fr rows collapse to
   0 and the frame is byte-identical. layout()'s budget is untouched: the
   fr rows only distribute what the solve already left over. */
#frame[data-port][data-narrow] #board{
  grid-template-rows:minmax(0,1fr) auto auto auto minmax(0,.25fr) auto 0;
  grid-template-areas:"." "hist" "game" "modes" "." "dock" "."}
/* THE TABLET EXCEPTION — the rendered B variant (768x1024): at a tablet's
   width the all-sky-above pose beached the cluster at the foot of a huge
   frame; a .6fr bottom row splits the slack 62/38 and the cluster reads
   centred-low, thumb zone kept. :not([data-narrow]) is layout()'s own
   w>=560 line — every portrait PHONE is narrow and keeps the waterline. */
#frame[data-port]:not([data-narrow]) #board{grid-template-rows:minmax(0,1fr) auto auto auto auto minmax(0,.6fr)}
/* THE SAFE AREA IS CONSUMED HERE (mobile round): viewport-fit=cover has
   been declared since the first build and nothing ever spent the insets.
   env() is 0 on every notchless frame, so this computes to the base rule
   exactly there; layoutPass subtracts the same four numbers from the
   budget (safeInsets), so the pair cannot disagree about where the board
   ends. */
#frame[data-port] #stage{
  padding:calc(var(--band,62px) + env(safe-area-inset-top,0px))
          calc(var(--pad,22px) + env(safe-area-inset-right,0px))
          calc(var(--pad,22px) + env(safe-area-inset-bottom,0px))
          calc(var(--pad,22px) + env(safe-area-inset-left,0px))}
