# 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 <path-within-game_assets>` 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.
