@@ -155,8 +155,22 @@ export class GameScene extends Container implements IScene {
155155 const screenWidth = this . app . screen . width ;
156156 const screenHeight = this . app . screen . height ;
157157
158- const availableWidth = screenWidth - IMAGE_GAP - UI_PADDING * 2 ;
159- const availableHeight = screenHeight - UI_PADDING * 2 - 80 ;
158+ // Vertical layout band. Above the images: padding + timer chip + gap +
159+ // floating StageTracker (header + pills + IMAGE_HUG_GAP=36). Below the
160+ // images: padding + floating Hint button + IMAGE_HUG_GAP=36. The
161+ // middle-row arrow buttons float at the canvas center and don't claim
162+ // layout space. Constants are kept conservative so the floating React
163+ // pieces never overlap the panels.
164+ const TOP_RESERVE = UI_PADDING + 36 + 16 + 108 ; // ≈180
165+ const BOTTOM_RESERVE = UI_PADDING + 76 ; // ≈96 (Hint reserve regardless of mode for stable image position)
166+
167+ // Horizontal layout band: subtract room for the floating prev/next
168+ // arrows that flank the image pair. ARROW_BUTTON (48) + ARROW_GAP (48)
169+ // each side, mirrored from src/ui/react/GameHud.tsx so the arrows
170+ // always fit even on narrow viewports.
171+ const ARROW_RESERVE = ( 48 + 48 ) * 2 ;
172+ const availableWidth = screenWidth - IMAGE_GAP - UI_PADDING * 2 - ARROW_RESERVE ;
173+ const availableHeight = screenHeight - TOP_RESERVE - BOTTOM_RESERVE ;
160174 const maxImageWidth = availableWidth / 2 ;
161175
162176 const scaleX = maxImageWidth / IMAGE_WIDTH ;
@@ -168,9 +182,22 @@ export class GameScene extends Container implements IScene {
168182
169183 const totalWidth = scaledWidth * 2 + IMAGE_GAP ;
170184 const startX = ( screenWidth - totalWidth ) / 2 ;
171- const startY = ( screenHeight - scaledHeight ) / 2 + 20 ;
185+ // Center the image within the available band — NOT the whole viewport —
186+ // so the floating tracker / hint above and below stay clear.
187+ const startY = TOP_RESERVE + Math . max ( 0 , ( availableHeight - scaledHeight ) / 2 ) ;
172188
173189 this . gameArea . position . set ( startX , startY ) ;
190+
191+ // Tell the React HUD where the rendered image pair lives so it can hug
192+ // the panels: the StageTracker sits just above `top`, the Hint sits just
193+ // below `bottom`, and the prev/next arrows in MiddleRow hug the left/right
194+ // edges based on `width`. Republished on every layout pass so resize
195+ // (and image-scale recalculation) keeps the HUD anchored.
196+ useUIStore . getState ( ) . setImagePairBounds ( {
197+ width : totalWidth ,
198+ top : startY ,
199+ bottom : startY + scaledHeight ,
200+ } ) ;
174201 }
175202
176203 private createPlaceholders ( ) : void {
@@ -201,16 +228,16 @@ export class GameScene extends Container implements IScene {
201228 this . uiLayer . addChild ( this . menuIcon ) ;
202229 }
203230
204- this . navButtons . position . set (
205- this . app . screen . width / 2 - 55 ,
206- this . app . screen . height - UI_PADDING - 50 ,
207- ) ;
231+ // NavButtons used to live as a Pixi widget at the bottom-center; the
232+ // React StageTracker now owns prev/next + per-pair jumps and exposes the
233+ // 5-dots-per-pair structure that Pixi text couldn't communicate. Keep
234+ // the instance around so existing setForceDisabled hooks remain valid,
235+ // but never add it to the scene graph.
208236 this . navButtons . setCallbacks (
209237 ( ) => gameState . prevImage ( ) ,
210238 ( ) => gameState . nextImage ( ) ,
211239 ) ;
212240 this . navButtons . updateState ( 0 , IMAGES_PER_GAME ) ;
213- this . uiLayer . addChild ( this . navButtons ) ;
214241 // HUD state (foundCount, currentImageIndex, opponentFoundCount, gameType)
215242 // was hydrated from gameState by ui.mountHud() in init() and stays in
216243 // sync via the store<->gameState bridge. Nothing to wire here.
@@ -750,7 +777,6 @@ export class GameScene extends Container implements IScene {
750777 resize ( width : number , height : number ) : void {
751778 this . setupLayout ( ) ;
752779 this . menuIcon . position . set ( width - UI_PADDING - 44 , UI_PADDING ) ;
753- this . navButtons . position . set ( width / 2 - 55 , height - UI_PADDING - 50 ) ;
754780 this . countdownOverlay . resize ( width , height ) ;
755781
756782 if ( this . placeholderContainer . children . length > 0 ) {
0 commit comments