# Combat Juice & the Three-Dimensional Board — build spec

*Decided 2026-09-13 from the combat-juice brainstorm (`design/mockups/combat-juice.html`, gallery §13). Visuals + exact demo numbers live in the mockup; this document is the build contract. Status: **J1 SHIPPED 2026-09-13** (fast-mode 🎬 chip, element-tinted damage numbers, check vignette/heartbeat/ember, new Sfx voices) — **J2 (three.js board) was BUILT 2026-09-14 and REVERTED the same day** after live play (user: "3D doesnt really look good") — the DOM board is canonical again; §3 below is kept as history, and §8's ladder resolves to **rung A**. **J3 (physical juice), if ever built, is DOM-only and diegetic (no full-screen overlays — see the standing rule above).**

> **§2 SUPER cut-in — REMOVED 2026-09-14.** After one live session the user removed it entirely ("too much flashing"). Kills play the shipped kit; §2's SUPER row stays as history, and the overkill ladder (T1–T4) remains approved for J3 — minus any screen-flash frames. The ☠/🛡 reticle and Throne-Duel finisher ceremony may return only in a diegetic (in-board) form if the user asks. **Standing rule: no full-screen flashes/overlays on board actions.**

## 1. Picks on record (user, 2026-09-13)

| # | System | Pick |
|---|---|---|
| 1 | SUPER cut-in | **Direction A — "Divine Decree"** (obsidian banner, gold edge, glyph emblem on a rotated diamond) |
| 2 | Overkill shake ladder | **Adopt as printed** (T1–T4 exact numbers below) |
| 3 | Element-reactive hits | **Approved + refinement:** the floating damage number **tints to the attacker's element** (fire `#ff6b4a`, water `#4aa3ff`, grass `#58c470`, light `#f5c84c`, dark `#8a5cf6`; neutral keeps the classic red `#ff5a4e`) |
| 4 | AEGIS "The Reforge" | Approved |
| 5 | Check & mate drama | Approved |
| 6 | Armored siege clang | Approved |
| 7 | Idle life | Approved |
| 8 | Backbone | **D — full three.js board, phased** (supersedes the A/B/C overlay ladder; rung C is absorbed) |

## 2. Approved systems — triggers & numbers (source of truth for behavior)

**SUPER (dir A).** Fires only on a **committed** attack with `dmg ≥ hp` — computed wherever `targetsFor`/`playableMove` already evaluate `resolveHit`; never on bounces, never on hover. Hover anticipation stays: red ☠ reticle + "SURE KILL · N dmg" tag vs gray 🛡 "BOUNCE · N dmg". Sequence: 80ms impact freeze → banner clip-wipe 180ms (glyph scale 1.15→1.0 with 1-frame judder at 120ms) → 300ms hold → white flash 70ms → cut to the board exactly on the impact frame. ~1.0–1.1s at ×1; **fast mode ×0.6** toggle (also the reduce-motion accommodation). AEGIS victims still fire (the body dies); king attacks always fire (Throne Duel finisher). Sting: noise riser 250ms → 130→55Hz brass thump + shimmer ring.

**Overkill ladder.** `r = dmg / hp` on the defender:

| Tier | r | hit-stop | shards | stage shake | tilt | zoom | extras |
|---|---|---|---|---|---|---|---|
| T0 BOUNCE | < 1 | — | — | glyph ±4px/260ms (current) | — | — | slash + damage pop |
| T1 CLEAN CUT | 1–1.49 | 0 | 12 | 2px/120ms | 0° | — | dust ring, 8 motes |
| T2 BRUTAL | 1.5–2.99 | 60ms | 16 | 5px/220ms | 0.6° | — | ember ring |
| T3 SAVAGE | 3–5.99 | 100ms | 24 | 9px/320ms | 1.2° | 1.02 | shockwave 1.4× square · tile crack · neighbors jostle 3px |
| T4 ANNIHILATION | ≥ 6 | 140ms | 34 | 14px/420ms + 6px dip | 2.0° | 1.03 | white→element flash 90ms · 5 radial cracks · neighbors 5px · 45Hz sub-thump |

Shake is a damped sine on the whole stage: `A·e^(−t/90ms)·sin(2π·20Hz·t + φ)`, fresh random phase per axis per hit, rotation at 0.35× amplitude. SUPER stacks on top (cut-in plays, then the tier pays out).

**Element hits.** Per hit (bounce and kill), in the attacker's element: fire = 12 embers + scorch decal fading 2s; water = 10 droplets + splash ring; grass = 8 leaf shards spiraling; light = 8 motes + ray flash 120ms; dark = 6 smoke puffs; neutral/none = the current white slash. Damage number tint per pick #3.

**AEGIS Reforge.** On a *legal* respawn: kill resolves normally → 12 gold shards spawn on a 1.5× ring and converge inward 260ms → white-gold flash 60ms → body pops scale 0.6→1.0 + ankh ring expands 1.5× + light pillar 220ms → halo fades 800ms. Sealed-by-check keeps the anti-ceremony (gray puff + "✦ AEGIS shattered").

**Check & mate.** Check: king tile rim pulses red 2×400ms, 25% red edge vignette 900ms, two 55Hz heartbeat thumps (0 / 260ms), king glyph ember glow 1.2s. Final-blow slow-mo: reserved for the capture that delivers checkmate (and Throne Duel's last hit) — capture at 0.35× time for 700ms real, zoom 1.04 toward the victim, slight desaturation, snap back, then the shipped Triumph Stage dialog.

**Siege clang.** Failed attack: attacker slides to 80%, 2 spark crosses + 4 shield chips, defender braces (pushed 2px, springs back), attacker recoil-arcs home with a slight hop; sfx = 3.2kHz high-Q metallic ring + knock body.

**Idle life.** Breathing ±1px (1.5s alternate, random phase per piece) with synced shadow; glide trails (3 afterimages, opacity ≤0.25, 40ms apart); landing dust (6 motes quiet / 10 on attack); camera drift ±3px / 8s. Reduce-motion switch kills breathing + drift; event-driven effects stay.

## 3. Target architecture — the three.js board (rung D)

The board stops being a DOM grid and becomes a three.js scene. UI chrome (navbar, lobby, army, dialogs, dev console) **stays DOM** — the Hearthstone split from asset-research.md §3.

- **Pieces:** GLB meshes from the existing pipeline (`assets/tools/render_pieces.py` → `assets/generated/glb/cg-*.glb`). Element/rarity are parameterized tints/emissives, not new models (asset-research §4). Max 32 meshes + shared materials; `InstancedMesh` per type if profiling demands.
- **Board:** 8×8 tile meshes on a plinth; d4/e4/d5/e5 arena highlight; selection/target rings as ring meshes; SUPER/bounce reticles as 3D rings on target tiles (the hover layer from §1).
- **Ownership frame:** brass corner ticks only on the mover's own pieces (locked principle, all game), as geometry/edge material — per-client rendering stays symmetric online.
- **Stats plates, HP bars, element badges, "?" chips: projected DOM overlay.** Each piece anchors an HTML plate projected per-frame from its 3D position. Rationale: text stays crisp, the locked board-readout components are reused as-is, and the a11y tree keeps living in the DOM (fixing the known RTL ordering quirk can ride along).
- **Camera:** fixed ¾ War Table angle; a camera *rig* group owns shake/tilt/dip/dolly so gameplay logic never touches camera math.
- **Interaction:** raycast picking with pointer events; drag & drop preserved (drag = piece follows a ground-plane intersection); click-to-move preserved; illegal = the two-damped-knock "no".
- **Fallback:** if WebGL init fails, the current DOM board ships untouched. Every FX system is written behind a thin `Juice` interface (trigger in, promise out) so DOM and 3D backends can both implement it.
- **Never:** the King pillar, check-holds, scouting model, or game-state flow change — `finishMove` still mutates after animation; the engine stays pure.

## 4. Where each system lives (screen-space vs scene)

| System | Lives in | Why |
|---|---|---|
| SUPER cut-in (dir A) | fullscreen DOM overlay | screen-space presentation, exactly like mobile games; ports 1:1 between backends |
| Damage numbers (element-tinted) | projected DOM overlay | text crispness + reuse |
| Check vignette | DOM overlay | screen-space |
| Hit-stop | the render loop clock | freezes everything, both backends |
| Shake / tilt / dip / zoom ladder | camera rig | real camera now — the ladder numbers map directly (px→world units at board scale) |
| Shards, element bursts, dust, trails, shockwave, cracks, ankh ring, light pillar, sparks/chips | 3D scene (particles/meshes/decals) | this is why D was picked |
| Breathing, jostle impulses | piece transforms | cheap per-frame sin |
| All new sounds (riser, boom, sub, clang, heartbeat, shimmer) | `Sfx` kit (WebAudio synth) | zero assets, proven in the mockup |

## 5. Build order — three phases, nothing built twice

- **J1 · Brain (screen-space + clock, on the current DOM board):** SUPER trigger plumbing (`targetsFor`/`playableMove`/`animateMove`), dir-A overlay, fast-mode toggle, hit-stop hook, new Sfx kit, element-tinted damage numbers, check vignette + heartbeat. All of it ports 1:1 to the 3D board later — zero waste.
- **J2 · Board (three.js foundation to parity):** scene, GLB pieces, tiles, raycast picking + drag & drop, projected plates/badges, ownership frames, camera rig, WebGL-fail fallback to the DOM board. Parity checklist in §6 gates the swap.
- **J3 · Body (3D-native juice, lands with/as the board):** the physical ladder (camera shake/tilt/dip/zoom, GPU shards, element particles, shockwaves, cracks, jostle), Reforge, slow-mo dolly, siege clang, trails/dust/breathing/drift. Rung C's overlay is skipped — absorbed by D.

Rationale: the approved systems split cleanly into *screen-space* (port 1:1, ship now) and *physical* (belong in 3D; building them on DOM first would be throwaway work). J1 gives instant juice while J2 is in flight.

## 6. Board-swap parity checklist (J2 exit gate)

Selection & legal-move targets · drag & drop + click · glide/bump motion · AEGIS marker + respawn positions · promotion picker trigger · scouted `?` chips · check/checkmate flow into Triumph Stage · siege target offering · threefold/siege hints · ownership frames · arena highlight · sound kit parity · a11y tree (squares reachable) · scrollbar-gutter/constant-height untouched · bot games + Throne Duel flows.

## 7. Performance & budget

First-paint target < 1.5 MB (asset-research §5): Draco/meshopt GLBs, KTX2 textures if used, one material atlas, `powerPreference: "high-performance"`, DPR clamp 2. Mobile floor: the DOM fallback covers WebGL-fail devices; a "lite" toggle (no particles, half-rate effects) if profiling demands. `prefers-reduced-motion` + the fast-mode toggle gate everything ambient.

## 8. Open items

1. ~~**Build go-ahead + phase order confirm**~~ RESOLVED: J1 shipped 2026-09-13 (cut-in removed 2026-09-14 per the no-fullscreen-flash taste rule); J2 was built and reverted — the DOM board stays; the three.js backbone awaits a fresh go-ahead.
2. GLB variant strategy for the roster (tint-only vs generated variants) — starts with tint-only. *(Roster is now the 800-card pool matrix / 806 binder cells — the old "46-card" count retired 2026-09-14.)*
3. Whether SUPER plays in bot-vs-bot AUTO mode (proposed: yes but fast-mode-forced; spectator still sees it). *(Moot while the SUPER cut-in is removed.)*
4. Kill-cam orbit angles for the eventual D-stage SUPER upgrade (dir A overlay now; camera fly-in later — separate polish pass after J3).
