Test now also asserts the menu is back to its original shape (no random-start button), the initial preview and thumb name match the random pick, Start Puzzle begins exactly that image, and the original 🎲 Random button still randomises the menu preview.
The menu always opened on the first artwork; now imageIndex is randomised right after loadArtwork(), so each visit to the picker shows a different initial puzzle. The existing Random/prev/next buttons are unchanged (previous random-start button reverted in 4f2d299). Test page + CDP driver verify 8 fresh page loads all land on a valid in-range image with its preview loaded, with variety across loads.
New '🎲 Start Random' button picks a random picture (never the current one when there's a choice) and starts the puzzle immediately, keeping the selected difficulty. Menu panel grows 820→890 to fit it. Headless-chromium test page + CDP driver verify: button placement, 8/8 random starts reach playing state, image variety, single-image guard, and difficulty preservation.
- __jig_test.html boots a Phaser game that loads JigsawGame with stubbed artwork/music caches and exposes window.__* helpers for driving piece grab/drop, scene inspection, and coordinate conversion.
- phaser.esm.js is a vendored webpack build of Phaser used as the "phaser" import map target so the harness can run without a package manager.
- Add pointerdown handler on each piece to forward the event to the central onTableDown resolver; without it, Phaser's topOnly=true consumes the click and the background never sees it, leaving pieces un-grabbable.
- Anchor the hit-area rectangle to the image's displayOrigin instead of (0,0) so the custom hit box tracks the piece's actual rendered position.
- Introduce group-based table assembly: grid-adjacent pieces snap together on the table, drag as one rigid unit, and lock onto the board as a block
- Extract join/lock rules into pure `resolveDrop` in JigsawLogic.js with a headless verification suite (tools/verifyJigsaw.js) covering fit guarantee, joining, locking, and win counting
- Replace the flat top-bar buttons with a "Menu ▾" dropdown panel to free the top-right corner for the shared site music HUD
- Integrate MusicPlayer soundtrack via getGameSoundtrack() and pin it to the fixed HUD camera
- Offset main camera scroll/zoom bounds by HUD_H so the play field
never slides beneath the fixed top bar
- Shift the scattered-piece tray down by HUD_H to keep pieces clear of
the target image and HUD
- Assign board panel, frame, and reference image to the main camera only
so the fixed HUD camera doesn't draw their corners into the screen
- Hide the faint full-picture reference on Hard and Legendary levels
- Show the target-slot ghost preview only on Easy while dragging a piece
- Store the selected difficulty on the scene for these checks
- Expand the play field to a 3840x2160 world so pieces can scatter widely and the player can explore
- Introduce MIN_ZOOM (0.5) so the entire field fits on screen, with zoom anchored to the cursor
- Add a second fixed HUD camera so the menu, stats bar, and win overlay stay in screen space while the board camera pans/zooms
- Route world objects and UI containers to their respective cameras via cameraFilter
- Update background, scatter tray, and camera clamping to use the new world dimensions
- New Phaser scene (JigsawGame) supporting 4 difficulty tiers (25–144 pieces),
image selection, timer, hint overlay, and win screen.
- Pure geometry module (JigsawLogic) generates seeded tab/blank outlines so
adjacent pieces mesh exactly; also provides deterministic scatter positions.
- Pieces are drawn to canvas textures with depth shading; drag-to-snap with a
ghost target, camera pan on empty table, and wheel/button zoom (1x–4x).
- Registered in gamesRegistry, main.js scene list, and GameRoomScene slug map;
game-icons.png updated for the new icon frame.