- NewGame: all three forms (quick / friends / hotseat) now use the
Profile sub-view's natural-flow .page (no forced height, no pinned-CTA
.grow/.fg scroll), so the screen's single .content scroll + --vvh handle
overflow and a focused name input scrolls into view cleanly — fixes the
keyboard blank-space / deep-scroll regression. One layout rule, one place.
- PinPad: pause 250ms after the 4th digit before the verdict, so the fill
(and the shake on a wrong PIN) reads as a deliberate response.
- e2e: typePin waits for empty dots before typing (robust to the pause).
NewGame offline 'with friends' now builds a local pass-and-play game:
- lib/roster.ts: keep-last-valid name + roster->seats (unit-tested).
- NewGame.svelte: master host-PIN gate (rows disabled until set),
'are you playing too?' prompt seating the host at row 0, 2-4 player
rows with inline name validation + optional per-seat PIN, add/remove
(remove behind the master PIN), variant + multiple-words, Start.
- PinPad: verification via a verify(pin) callback (UI-held lock at
creation, source-held in-game) instead of a lock prop.
- i18n: hotseat.* (en + ru); the offline mode selector is now shown offline.
Groundwork for offline pass-and-play (hotseat) seat/host PIN locks:
- lib/pin.ts: salted SHA-256 PinLock (newSalt/hashPin/newLock/verifyPin);
a social lock for a shared device (documented, not cryptography).
- components/PinPad.svelte: 4-digit keypad (set/verify/change), auto-submit
on the 4th digit, shake on mismatch, hardware-keyboard support.
- i18n: pin.* keys (en + ru).
Engine/source/UI wiring follows.