diff --git a/HANDOFF.md b/HANDOFF.md index fec5899..01efb01 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -182,11 +182,13 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu because the wave grows with `hordeGrow` while the panic button was a flat 1 s; he chose scaling the melee over shrinking the horde. **Measure the crowd BEFORE calling `meleeSwipe`** — it kills everything in reach, so counting after counts the survivors and the cooldown barely moves. -- **The safe area is handled in ONE place: index.html's canvas rule.** The canvas is `position: fixed` - and inset by `env(safe-area-inset-*)`, so the whole game sits inside the safe region — nothing ever - renders under the notch, the status bar or the home indicator. `render.js` does NOT offset the HUD as - well (`SAFE` stays at zero); doing both subtracts the notch twice. Full-bleed with an inset HUD was - tried first and Oliver rejected it twice. +- **The safe area is handled ASYMMETRICALLY, on purpose.** TOP: index.html insets the canvas element + below the status bar, because that is opaque UI the scoreboard must clear - `SAFE.top` stays 0, and + adding a HUD offset there as well subtracts the notch twice. BOTTOM: the canvas deliberately runs to + the PHYSICAL bottom edge so the court art fills the screen (the home indicator is a slim translucent + bar, not opaque UI); only the interactive RACK and the draw meter lift clear of it, via + `SAFE.bottom`, which `measureSafe` reads from the hidden `#safe` probe (env() is CSS-only). + Reserving the bottom inset on the element instead just left a dead band. - **The canvas is STRETCHED by CSS (100vw x 100dvh minus insets) and `fitViewport` derives the logical grid from the box it was actually given** — `cv.clientWidth/clientHeight`, never `innerWidth/innerHeight`. Sizing it the other way round (compute a scale, then set explicit px) laid it out against whatever diff --git a/index.html b/index.html index 92ef847..7978b89 100644 --- a/index.html +++ b/index.html @@ -31,15 +31,16 @@ explicit px) meant it was laid out against whatever innerHeight reported at load — before iOS Safari's toolbars had settled — and it stayed a strip short of the bottom forever after. 100dvh is the dynamic viewport height, so it tracks those toolbars. */ - /* The canvas lives ENTIRELY INSIDE the safe area — never under the notch, the status bar or the - home indicator. Doing the inset here rather than offsetting the HUD in canvas space keeps it in - ONE place: render.js then just matches its logical grid to whatever box it was given. + /* TOP inset only. The status bar is opaque UI and the scoreboard must clear it, so the canvas starts + below it — but the home indicator is a slim translucent bar, so the canvas runs all the way to the + PHYSICAL bottom edge and the court art fills it. Only the weapon RACK lifts clear of the indicator, + via SAFE.bottom in render.js. Reserving the bottom inset here instead just left a dead band. 100dvh is the dynamic viewport height, so it tracks iOS Safari's toolbars appearing/hiding. */ canvas { image-rendering: pixelated; display: block; position: fixed; touch-action: none; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px); width: calc(100vw - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)); - height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px)); } + height: calc(100dvh - env(safe-area-inset-top, 0px)); } #hint { position: fixed; bottom: 8px; width: 100%; text-align: center; font: 400 10px Silkscreen, monospace; color: #6b7688; } @media (pointer: coarse) { #hint, #intro .kb { display: none; } } @@ -79,6 +80,6 @@
TAP TO START
- +