# AGENTS.md These requirements apply to every screen, feature, game system, and code change in this repository. ## Product priority AYN Thor Android app is the primary product. Browser build is a secondary access path for players without an AYN Thor. Do not weaken the Thor experience to simplify the browser version; provide an adaptive browser fallback instead. ## Game asset workflow - `game_assets/` is an ignored local source library. Never load or import a runtime asset directly from it. - When adopting a model, animation, texture, audio file, or other game asset from `game_assets/`, copy it into the tracked `src/assets/game/` runtime library first. Preserve its path relative to `game_assets/`. - Use `pnpm assets:import ` for the copy. Use `--replace` only to deliberately update an existing runtime copy. Copy required companion files and license/attribution notices with assets that are not self-contained. - Reference only `src/assets/game/` copies from game code. Keep `game_assets/` as source material for future imports, conversions, and authoring work. ## AYN Thor display and layout targets Design and test against both physical displays in landscape orientation. Physical panel resolution and Android CSS/layout viewport are different concepts; record and validate both. | Display | Panel specification | Android CSS/layout viewport | | --- | --- | ---: | | Main / top | 6-inch AMOLED, 1920 x 1080 physical pixels, 120 Hz | Approximately 960 x 540 CSS pixels | | Secondary / bottom | 3.92-inch AMOLED, 1240 x 1080 physical pixels, 60 Hz | Approximately 620 x 540 CSS pixels | - `1920 x 1080` and `1240 x 1080` describe physical framebuffer/panel pixels. They are not the UI layout viewports. - All Thor UI sizing, breakpoints, typography, hit targets, navigation geometry, and visual QA must use the Android CSS/layout viewport: approximately `960 x 540` for the main display and `620 x 540` for the secondary display. - Use physical resolution only for render-buffer quality, pixel density, screenshot validation, and GPU/performance decisions. - Ship gameplay at a stable, locked 60 FPS on both displays. The main panel can refresh at 120 Hz, but 60 FPS is the required game target. - Treat the displays as two independent surfaces with different aspect ratios, not one continuous `3160 x 1080` canvas. - Use each display's own container or viewport dimensions for layout and typography. Never size bottom-screen UI from the top screen or combined page viewport. - Approximate CSS viewports reflect current Android scaling. Runtime code must still measure actual viewport size, device pixel ratio, safe insets, and Android system-bar insets rather than assuming those values are constant. - Keep critical controls and information inside safe bounds. No clipping, overlap, unreadably small text, or off-screen focus state at either target size. - Any new full-screen view, menu, modal, HUD, overlay, loading state, and error state must be checked at both CSS/layout viewports and their corresponding physical framebuffer resolutions. ## Dual-screen behavior - Use both Thor screens whenever the second display can add useful information or controls without duplicating clutter. - Top screen owns primary 3D gameplay, world presentation, cinematics, and information needed while looking at the action. - Bottom screen should host contextual/tactical UI such as party state, abilities, maps, inventory, quests, chat, loadouts, and settings where appropriate. - Actions and state must stay synchronized across displays. Moving a feature to the bottom screen must not hide required feedback from the top screen. - Keep rendering and update work independent so an expensive bottom-screen UI update cannot stall top-screen gameplay. - Support display attach, detach, recreation, sleep, and resume without losing game state or duplicating input handlers. - Browser/single-display mode must preserve all features through responsive panels, tabs, drawers, or overlays. Dual-screen layout remains preferred on Thor. ## Controller-first navigation Every screen must be fully operable with the built-in controller. Touch, mouse, and keyboard are optional alternate inputs. - No action may require tapping, clicking, hovering, or focusing the canvas/DOM first. - Do not depend on browser/DOM focus for gamepad routing. Poll and route controller input through one app-level input service, then send semantic actions to gameplay and UI. - Every interactive item must be reachable in a deterministic order and show a clear visible focus state. - Directional navigation must match spatial layout. Define explicit neighbors when automatic spatial navigation would be ambiguous. - Provide controller paths for confirm, cancel/back, tab or section changes, scrolling, sliders, dialogs, tooltips, pause, and return to gameplay. - Trap navigation inside an open modal, then restore focus to the element that opened it when the modal closes. - Choose a safe default focus target whenever a screen opens or the active display changes. Never leave focus nowhere or behind an overlay. - Prevent held buttons and analog drift from causing uncontrolled repeats. Use dead zones, initial repeat delay, repeat rate, debouncing, and one-shot press edges as appropriate. - Clear held-input state on controller disconnect, app pause, visibility change, screen recreation, and display handoff. - Keep controller mappings centralized and remappable. UI prompts must reflect the active controller mapping. - Avoid text entry for required flows where possible. When required, provide an Android/controller-accessible input path. - Browser fallback must not require a click to focus individual controls or the game canvas. Browser/OS restrictions may require the browser itself to be active and may require one initial user gesture before the Gamepad API is exposed; no game code may add further focus requirements. ## Modular game architecture Build every game mechanic as a reusable, composable module. Repeated mechanic code is a defect. - Separate game rules and state from rendering, React components, display routing, input devices, audio, persistence, and networking. - Expose mechanics through small typed interfaces and semantic commands/events. Do not let a mechanic read DOM state or hard-code a specific screen. - Make abilities, effects, enemies, encounters, items, quests, and UI bindings data-driven when behavior differs mainly by configuration. - Prefer shared systems and composition over copied handlers, large conditional components, or deep inheritance. - Keep one authoritative state model. Both displays render projections of shared state rather than maintaining competing copies. - Put shared timing, cooldown, targeting, resource, status-effect, damage, healing, and validation logic in domain modules usable by players, AI, previews, tooltips, and tests. - Inject platform-specific adapters for Android, browser, storage, display, input, and network behavior. - Add focused tests for reusable mechanics and regression tests for bugs. A mechanic should be testable without launching the renderer or mounting UI. - Before adding code, search for an existing system that can be extended. If similar logic appears twice, extract the common behavior unless doing so would create a less coherent abstraction. ## Performance requirements Stable locked 60 FPS is a release requirement, not a best-effort goal. - Treat the AYN Thor Lite configuration (Snapdragon 865, 8 GB RAM) as the performance floor unless project requirements explicitly change. - Maintain a 16.67 ms total frame budget during real gameplay, including simulation, scripting, rendering, UI, and cross-display synchronization. - Profile representative gameplay on physical Thor hardware. Desktop-only measurements are insufficient for performance-sensitive changes. - Measure before and after substantial changes. Track frame time, 1% lows, long frames, CPU time, GPU time, JavaScript heap, native memory, draw calls, triangles, texture memory, and garbage-collection pauses where tooling permits. - Do not allocate objects, arrays, closures, materials, textures, or temporary vectors in hot per-frame paths when reusable storage or pooling is practical. - Dispose GPU, audio, event-listener, timer, subscription, and display resources deterministically when their owner unloads. - Use culling, LOD, instancing, batching, compressed textures, bounded particle counts, and asset streaming/lazy loading where they measurably help. - Keep simulation work bounded. Avoid unbounded searches, per-frame full-collection scans, unnecessary polling, and update loops for invisible or inactive entities. - Prevent avoidable React renders. Subscribe to narrow state slices, memoize only where measured or structurally useful, batch updates, and throttle non-critical bottom-screen telemetry. - Load heavy screens and assets on demand, but prefetch predictable transitions when memory budget allows. - Avoid duplicate assets and duplicate state in memory across displays. Share immutable resources where platform APIs allow. - Favor algorithms and data layouts that reduce CPU and RAM together. Do not trade a large permanent memory increase for a tiny unmeasured CPU gain. - Performance fixes must preserve deterministic rules, input responsiveness, visual clarity, and controller accessibility. ## Verification scope - Verify the changed code and its direct dependencies. Do not run tests, visual QA, or controller checks for unrelated screens or systems by default. - For a screen change, check that screen's applicable display layouts, browser fallback, and controller path only. - For a reusable mechanic or domain change, run its focused tests plus direct consumers affected by the change. - Run viewport, display-layout, and browser-fallback testing only when a change adds or modifies a screen, menu, modal, HUD, overlay, or other UI/layout behavior. - Combat rules, encounter mechanics, world-space telegraphs, animations, VFX, tuning, and other gameplay-only changes do not require viewport testing unless they also change UI or screen layout. - Expand to broader regression or full-suite verification only for shared foundations, cross-cutting changes, risky refactors, release validation, or when explicitly requested. - State what was verified and any deliberately unverified scope in the handoff. ## Definition of done A screen or UI change is not complete until: 1. Its main-display UI is designed and verified at the approximately `960 x 540` Android CSS/layout viewport, with rendering validated against the `1920 x 1080` physical panel. 2. Its applicable secondary UI is designed and verified at the approximately `620 x 540` Android CSS/layout viewport, with rendering validated against the `1240 x 1080` physical panel. 3. It has a complete single-display browser fallback. 4. Every action is reachable and understandable using only a controller, with no click-to-focus step. Every code change is not complete until: 1. Shared game logic is modular, typed, and testable outside the renderer/UI. 2. It introduces no known resource leak, unbounded work, or avoidable hot-path allocation. 3. Representative Thor hardware sustains the locked 60 FPS target, or the change includes measured evidence and an explicit approved exception. Gameplay-only mechanic and domain changes do not inherit the screen/UI viewport requirements above.