The menu deals itself in

The splash's elements fly in from the left on the boot, 70ms apart, on the same
easeOutBack overshoot the canvas modals have always used. It hangs off
:not(.busy), so taking the loading state off IS the trigger and it never plays
again — returning from a run should not make you wait for a flourish you have
seen. Reduced motion turns it off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-31 13:22:16 +02:00
1 parent 380f98f3a3
commit aab83dacdc
2 files changed
+29

No files matched your search

+6
View File
@@ -595,6 +595,12 @@ can add the class, and Oliver kept seeing `LEVEL 1 · LEAGUE 1` snap to the real
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.
**THE MENU DEALS ITSELF IN.** The canvas modals have always arrived on an easeOutBack overshoot;
the splash now does the same once, on the boot: plaque, fixture, OTHER GAMES, the pair, the build
line fly in from the left 70ms apart (`@keyframes dealIn`, hung off `#splash:not(.busy)` so the
class removal IS the trigger). It fires on the boot only — coming back from a run must not make
you wait for a flourish you have already seen — and `prefers-reduced-motion` turns it off.
**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
+23
View File
@@ -519,6 +519,29 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat
/* the splash's bottom band: no bar, no rule — the court art has to keep running under it */
.foot.bare { border: 0; background: none; }
.foot.bare .btn.big { margin-bottom: 10px; }
/* THE DEAL-IN (2026-08-31, Oliver: "es wäre toll, wenn sich die buttons nach dem laden aufbauen
würden — arcade style"). The canvas modals have always arrived on an easeOutBack overshoot
rather than decelerating politely into place; this is that same snap, in CSS, for the one
moment the menu has an entrance to make. It fires ONCE, on the boot: the rule hangs off
:not(.busy), so it triggers when main.js takes the loading state off and never again — coming
back from a run must not make you wait for a flourish you have already seen. */
@keyframes dealIn {
from { opacity: 0; transform: translateX(-56px); }
60% { opacity: 1; }
to { opacity: 1; transform: translateX(0); }
}
#splash:not(.busy) #plaque,
#splash:not(.busy) .foot.bare > * {
animation: dealIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) backwards; }
#splash:not(.busy) #plaque { animation-delay: 0.02s; }
#splash:not(.busy) #mStart { animation-delay: 0.12s; }
#splash:not(.busy) #mOther { animation-delay: 0.19s; }
/* the pair of secondary buttons is a CLASS, so it needs the same weight as the shorthand above
to override its delay — the id selectors around it get there on their own */
#splash:not(.busy) .foot.bare > .two { animation-delay: 0.26s; }
#splash:not(.busy) #build { animation-delay: 0.33s; }
@media (prefers-reduced-motion: reduce) {
#splash:not(.busy) #plaque, #splash:not(.busy) .foot.bare > * { animation: none; } }
/* the boot bar wears the stats screen's kill bar, doing duty as a loading bar. While #splash is
.busy it stands in the buttons' place — same band, same height, so nothing jumps when it goes. */
#boot { display: none; margin-bottom: 10px; }