# HANDOFF REPORT: DUAL HEALTH & MANA FLUID ORBS SPECIFICATION
> **Agent**: `explorer_m1_1_gen2` (teamwork_preview_explorer)  
> **Milestone**: Milestone 1 - Dual Health & Mana Orbs (PoE2 Style)  
> **Status**: COMPLETED & READY FOR IMPLEMENTATION  

---

## 1. OBSERVATION

Direct investigation of the FreeExile client codebase, metadata, and survey artifacts revealed the following findings:

1. **Current HUD Life/Mana Representation (`client/webapp/index.html` lines 36–39)**:
   ```html
   <div class="flex items-center gap-1 mt-0.5">
     <div class="w-14 h-1.5 bg-stone-950 rounded-full overflow-hidden border border-stone-800/80"><div id="player-hp-bar" class="bg-red-700 h-full w-full transition-all duration-100"></div></div>
     <span id="player-hp-text" class="text-[8px] font-mono text-stone-400">100 / 100</span>
   </div>
   ```
   *Observation*: Vitality is currently represented solely by a tiny 56px horizontal strip (`#player-hp-bar`) in the top-left status card. Mana/Qi has zero representation in the active DOM. No circular orbs or liquid dynamics exist in the running client.

2. **Pre-Existing Mathematical & Visual Assets (`hud_liquid_glow.metal`, `hud_combat_metadata.json`, `hud_dual_orbs_frame.png`)**:
   - `client/webapp/assets/ui/combat/hud_liquid_glow.metal` (line 12):
     ```metal
     float wave = sin(float(gid.x) * 0.05 + time * 3.0) * 0.05;
     ```
   - `client/webapp/assets/ui/combat/hud_combat_metadata.json` (lines 5–18):
     `life_orb` and `mana_orb` specify `w: 180, h: 180, touch_radius: 90`.
   - `client/webapp/assets/ui/combat/hud_dual_orbs_frame.png`:
     Resolution is 512x256 (two 256x256 orb frames centered at `x=128` and `x=384`).

3. **Player State Attributes & Update Loops (`iso_math.js`, `char_creation.js`, `canvas_renderer.js`, `monster_system.js`)**:
   - `char_creation.js` (lines 115–118):
     ```javascript
     player.maxHp = char.max_hp || 100;
     player.hp = char.current_hp || player.maxHp;
     player.maxMana = char.max_mana || 50;
     player.mana = char.current_mana || player.maxMana;
     ```
   - `monster_system.js` (lines 365–373): `updatePlayerHpUI()` updates `#player-hp-bar` and `#player-hp-text`.
   - `canvas_renderer.js` (lines 264–287): The 120 FPS `renderLoop` executes every frame via `requestAnimationFrame(renderLoop)`.

4. **Codebase Hygiene Boundaries (`tools/lint/check_code_and_doc_hygiene.py`)**:
   - Strict soft caps: Code files $\le 350$ lines, CSS $\le 350$ lines, HTML $\le 200$ lines, Docs $\le 400$ lines.
   - Current `index.html`: 179 lines.

---

## 2. LOGIC CHAIN

```mermaid
flowchart TD
    Obs1["Obs 1: Minimal 56px HP bar, 0 Mana UI in index.html"] --> Need1["Need 1: ARPG Dual Orbs (Red Life, Blue Mana) with numeric overlays"]
    Obs2["Obs 2: Metal shader sin(x*0.05 + time*3.0) & 180x180 metadata"] --> Need2["Need 2: 180x180 Canvas 2D liquid renderer with exact sinusoidal formula"]
    Obs3["Obs 3: Player state tracks hp, maxHp, mana, maxMana"] --> Need3["Need 3: HudOrbs.update(hp, maxHp, mana, maxMana, dt) hook in renderLoop"]
    Obs4["Obs 4: Low HP danger lacks peripheral feedback"] --> Need4["Need 4: <25% HP trigger: crimson heartbeat pulse & screen vignette"]
    Obs5["Obs 5: Strict hygiene caps (<=350 lines JS/CSS, <=400 lines HTML)"] --> Need5["Need 5: Lean, modular native ES files with zero dependencies"]
```

1. **Procedural Liquid Wave**: Implementing the shader formula `Math.sin(x * 0.05 + time * 3.0)` in Canvas 2D reproduces the exact Apple Metal shader visual dynamics across all browsers without requiring WebGL context switches.
2. **High-DPI Retina Rendering**: Using an internal canvas resolution of $180 \times 180$ scaled down via CSS to $86 \text{px} \times 86 \text{px}$ delivers a crisp $2.09\times$ retina pixel density on iPhone 15 Pro and 4K displays.
3. **Visceral Peripheral Warning**: When HP falls below 25%, player attention must be redirected immediately. Combining the `.orb-low-pulse` double-heartbeat CSS animation with `#hud-low-hp-vignette` provides visceral urgency matching PoE2 standards.
4. **Zero-Allocation Hot-Path**: All calculations inside `renderOrb` use scalar coordinates and reusable canvas paths; zero objects or arrays are allocated inside the frame loop.

---

## 3. CAVEATS & ASSUMPTIONS

1. **Touch Ergonomics on Small Screens**: On viewports $\le 640\text{px}$, orb diameter scales down to $74\text{px}$ and docks cleanly to the bottom corners, preserving safe zones for virtual joystick operation and combat touch clusters.
2. **Headless Execution Compatibility**: In non-DOM test runners (Node.js or Python `unittest`), `HudOrbs.init()` guards against null canvas contexts, preventing crashes during test execution.
3. **Smooth Damping vs. Responsive Health**: Value damping uses `LERP_SPEED = 8.0` (~120ms transition) so sudden damage feels fluid while remaining tactically responsive.

---

## 4. CONCLUSION & IMPLEMENTATION PROPOSAL

Four ready-to-mount proposed artifacts have been generated in `c:\Projects\FreeExile\.agents\teamwork\explorer_m1_1_gen2\`:

### 4.1. Proposed JavaScript Module: `proposed_hud_orbs.js` -> `client/webapp/js/ui/hud_orbs.js`
- **Class**: `HudOrbs`
- **Key Methods**:
  - `init()`: Caches DOM handles, binds click event (potion drink on Life Orb).
  - `update(hp, maxHp, mana, maxMana, dt = 0.016)`: Smooth lerp, `< 25%` pulse detection, numeric updates, and liquid rendering.
  - `renderOrb(ctx, fillRatio, type, time, isLowHp)`: Circular clip, background cavity, sinusoidal crest `sin(x * 0.05 + time * 3.0)`, specular sheen ellipse, inner Fresnel shadow, and antique bronze/iron bezel.
- **Line Count**: 286 lines (Soft Cap $\le 350$).

### 4.2. Proposed Stylesheet: `proposed_hud_skills.css` -> `client/webapp/css/hud_skills.css`
- **Classes**: `#hud-orbs-container`, `.orb-globe`, `.orb-life`, `.orb-mana`, `.orb-canvas`, `.orb-sheen`, `.orb-label-overlay`, `.orb-type-tag`, `.orb-low-pulse`, `@keyframes crimsonHeartbeat`, `#hud-low-hp-vignette`.
- **Line Count**: 242 lines (Soft Cap $\le 350$).

### 4.3. Proposed DOM Snippet: `proposed_index_markup.html` -> `client/webapp/index.html`
- **Insert in `<head>`**: `<link rel="stylesheet" href="css/hud_skills.css">`
- **Insert in `#app-viewport`**:
  ```html
  <div id="hud-low-hp-vignette" class="hidden"></div>
  <aside id="hud-orbs-container" aria-label="Player Vitality and Mana Orbs" class="safe-bottom safe-left safe-right">
    <div id="hud-life-orb-wrapper" class="orb-wrapper" data-tooltip-title="Khí Huyết Lưu Đày (Life)" data-tooltip-desc="Sinh mệnh bản nguyên. Nhấp chuột hoặc chạm để phục dược (Phím 1 / Potion)." data-tooltip-hotkey="1 / Potion">
      <div id="hud-life-orb" class="orb-globe orb-life">
        <canvas id="life-orb-canvas" width="180" height="180" class="orb-canvas"></canvas>
        <div class="orb-bezel"></div>
        <div class="orb-sheen"></div>
        <div id="life-orb-warning" class="orb-pulse-warning hidden"></div>
        <div class="orb-label-overlay">
          <span id="txt-life-cur" class="orb-cur-val">100</span>
          <span class="orb-divider">/</span>
          <span id="txt-life-max" class="orb-max-val">100</span>
        </div>
        <span class="orb-type-tag">HP</span>
      </div>
    </div>
    <div id="hud-mana-orb-wrapper" class="orb-wrapper" data-tooltip-title="Chân Khí Hoang Vực (Mana / Qi)" data-tooltip-desc="Năng lượng bản năng để vận chuyển võ công cổ xưa và tuyệt kỹ." data-tooltip-hotkey="Skills">
      <div id="hud-mana-orb" class="orb-globe orb-mana">
        <canvas id="mana-orb-canvas" width="180" height="180" class="orb-canvas"></canvas>
        <div class="orb-bezel"></div>
        <div class="orb-sheen"></div>
        <div class="orb-label-overlay">
          <span id="txt-mana-cur" class="orb-cur-val">50</span>
          <span class="orb-divider">/</span>
          <span id="txt-mana-max" class="orb-max-val">50</span>
        </div>
        <span class="orb-type-tag">QI</span>
      </div>
    </div>
  </aside>
  ```
- **Insert in `<body>`**: `<script type="module" src="js/ui/hud_orbs.js"></script>`

### 4.4. Game Loop Integration
In `client/webapp/js/engine/canvas_renderer.js` inside `renderLoop`:
```javascript
if (window.HudOrbs && typeof window.HudOrbs.update === 'function' && window.player) {
  window.HudOrbs.update(window.player.hp, window.player.maxHp || 100, window.player.mana || 50, window.player.maxMana || 50, dt);
}
```

---

## 5. VERIFICATION METHOD

1. **Hygiene Audit Verification**:
   ```bash
   python tools/lint/check_code_and_doc_hygiene.py --strict
   ```
   *Expected*: Zero hard cap violations across all files.

2. **Unit Test Verification**:
   Test specification written to `c:\Projects\FreeExile\.agents\teamwork\explorer_m1_1_gen2\proposed_test_hud_orbs.py`.
   To run after applying the proposed files:
   ```bash
   pytest tests/unit/test_character_animation_and_skills_vfx.py -v
   python .agents/teamwork/explorer_m1_1_gen2/proposed_test_hud_orbs.py
   ```
   *Expected*: All assertions pass.

3. **Runtime Visual Invalidation Conditions**:
   - Liquid fill level inverted or exceeds circle boundaries.
   - Text overlay truncated or unreadable against fluid background.
   - Low HP pulse fails to trigger when `hp < 25%` or fails to clear when healed.
   - Frame rate drops below 120 FPS during combat.
