diff --git a/PROJECT.md b/PROJECT.md index 83ed7af..4907a42 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -589,6 +589,17 @@ door beside it lost its subject problem: an unqualified `PLAY AGAIN` next to it "start something", so it names its mode — `GORE AGAIN` / `CLIMB AGAIN` / `CLASSIC AGAIN` / `TRAIN AGAIN`, derived in the summary from G.classic / G.match.train / G.match. +**THE BOOT STATE SHIPS IN THE MARKUP.** Hiding the plaque behind `#splash.busy` was not enough: +this is a MODULE script, so the browser paints index.html — placeholders and all — before main.js +can add the class, and Oliver kept seeing `LEVEL 1 · LEAGUE 1` snap to the real values. `class="busy"` +is in the HTML now and main.js only ever takes it off. The keyboard-hint strip hides with it +(`body:has(#splash.busy) #hint`), so the loading screen really is court, title and bar. + +**EVERY SCREEN OPENS AT ITS TOP.** `#intro` is the one scroller the menus share, so a scroll earned +on the stats screen outlived it — go back and the splash came up scrolled, buttons off screen +(Oliver). `go()` resets `intro.scrollTop`, at the single funnel every stage change already passes +through. + Open for the phone: whether the fixture button should ever go to the setup screen (it does not now), and whether the promotion's WALK ON should enter the cup instead of the clubhouse. diff --git a/index.html b/index.html index 00dd47d..8732dd5 100644 --- a/index.html +++ b/index.html @@ -116,6 +116,9 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat .qBox p { margin: 0 0 6px; font: 400 12px Silkscreen, monospace; color: var(--cream); text-align: center; letter-spacing: 1px; } @media (pointer: coarse) { #hint { display: none; } } + /* ...and it is UI like any other: gone while the splash is still loading (the keyboard hints + live outside #intro, so this reaches them through the ancestor) */ + body:has(#splash.busy) #hint { display: none; } /* ---- THE MENU PALETTE. The canvas HUD keeps its own blue-grey and gold; the menus are deliberately a different room — dark court green, one acid accent, and one crimson reserved for the thing you actually came here to press. Five names so a repaint is five edits, not fifty. */ @@ -643,7 +646,12 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat same backdrop with two buttons, so it read as the title screen failing to advance. --> -
diff --git a/src/main.js b/src/main.js
index 8fd1dfd..99dc769 100644
--- a/src/main.js
+++ b/src/main.js
@@ -84,7 +84,17 @@ let pickedTour = remembered('tour') || TOURS[0].id;
// every stage change is a gesture, so it is also a safe place to (re)start the theme. The setup
// screen syncs its picker rows on entry: the identity screen writes the same picked* state, and
// rows that only ever toggle on their OWN taps would keep showing the boot-time selection.
-const go = stage => { if (stage === 'setup') syncSetup(); if (stage === 'train') buildTrain(); intro.dataset.stage = stage; menuMusic(); };
+// EVERY SCREEN OPENS AT ITS TOP (2026-08-31, Oliver: scroll the stats to the bottom, swipe back,
+// and the splash comes up scrolled with its buttons off screen). #intro is the one scroller the
+// menus share, so its offset outlives the stage that earned it unless it is reset here — the one
+// place every stage change goes through.
+const go = stage => {
+ if (stage === 'setup') syncSetup();
+ if (stage === 'train') buildTrain();
+ intro.dataset.stage = stage;
+ intro.scrollTop = 0;
+ menuMusic();
+};
// --- MODE selection in TWO STEPS (config.MENU + config.MODES): three posters on top, and the
// picked one deals its own second step underneath — classic deals the difficulty chips, zombie
@@ -976,7 +986,9 @@ setTimeout(() => { profileDone = true; }, 4000);
// whatever the fallback was. onerror counts too (render.js), so a dead network still finishes the
// bar rather than parking the player on it forever.
const splash = document.getElementById('splash'), bootFill = document.querySelector('#boot i');
-splash.classList.add('busy');
+splash.classList.add('busy'); // belt and braces: the class ships in index.html so the very
+ // first paint is already the loading state — this only matters if
+ // that ever gets dropped from the markup
const loaded = () => !splash.classList.contains('busy');
// setInterval, NOT requestAnimationFrame: rAF does not fire in a hidden tab, and a phone that
// launches the app and locks — or an install that loads in the background — would come back to a