Polish pass before final acceptance. The 8x8 grid was rendering flush to the left/right screen edges (gems ~4pt from the bezel on iPhone 17). Add a content margin (BOARD_INSET_X = 16pt) layered on top of the platform safe-area insets so the grid is framed by the background, while the HUD keeps using the bare safe insets so it still hugs the top below the Dynamic Island. The grid is width-constrained in portrait, so this inset is what sizes it; vertical centering inside the safe area is unchanged, and the win/lose banner (derived from the grid) stays centered over the framed board. Safe-area verified on a current iPhone simulator (iPhone 17, iOS 26): HUD below the Dynamic Island, board far above the home indicator, forced win/lose banners centered and unclipped. The headless geometry tests (hit_test, banner_layout) call compute() with a zero inset directly, so they are unaffected; full logic gate stays green (18/18). Goldens: add p9_polish.png (resting board, M3TE_ANIM_TIME=0) as the canonical polished layout. Re-capture the README-documented deterministic goldens whose board position shifts by the 16pt margin (p4_board, p4_hud, p6_idle_t0, p6_idle_mid, p6_select, p7_win, p7_lose, p7_restart). The in-flight move-timeline goldens (p5_swap_*, p6_anim_*, p6_fx_*, p6_inputlock_board) and the p0 quad goldens are not reproducible via the documented env pins (which pin only the idle clock + level state), so they are left as-is.
3.7 MiB
1206x2622px
3.7 MiB
1206x2622px