diff --git a/plans/260421-2042-level-redesign-solvability/phase-01-scrollable-viewport.md b/plans/260421-2042-level-redesign-solvability/phase-01-scrollable-viewport.md
new file mode 100644
index 0000000..c14b0e2
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-01-scrollable-viewport.md
@@ -0,0 +1,64 @@
+# Phase 1: Scrollable Viewport
+
+## Context
+- Brainstorm: `plans/reports/brainstorm-260421-2042-level-redesign-solvability.md` §5
+- Board component: `src/components/GameBoard.svelte` (90 LOC)
+- Game scene: `src/scenes/Game.svelte` (409 LOC)
+
+## Overview
+- **Priority:** CRITICAL — blocks all grid >9x9 redesigns
+- **Status:** pending
+- Prereq for Acts 3–6 (grids 10x10–13x13). Desktop-first; no mobile handling.
+
+## Key Insights
+- Current rendering assumes full grid fits on screen → unplayable at 12x12+.
+- Simple solution: CSS scroll container + `scrollIntoView` on player cell each turn.
+- Svelte 5 runes: `$effect(() => ...)` to react to player position.
+
+## Requirements
+- Viewport shows ~9x9 cells. Grid larger than viewport scrolls.
+- After each player move, scroll so player cell is visible (prefer centered).
+- Smooth scroll (CSS `scroll-behavior: smooth`).
+- No layout break at grids ≤8x8 (viewport = grid size, no scroll).
+
+## Architecture
+- Wrap `GameBoard.svelte` grid in a scrollable container `
`.
+- Fixed viewport size (e.g., `min(80vh, 720px)` square).
+- Inner grid expands naturally based on rows × cols × cellSize.
+- `$effect` triggers `cell_element.scrollIntoView({ block: 'center', inline: 'center', behavior: 'smooth' })` on `player.row`/`player.col` change.
+
+## Related Files
+- **Modify:** `src/components/GameBoard.svelte` (add viewport wrapper, scroll effect)
+- **Modify:** `src/styles/theme.css` (viewport CSS vars if needed)
+- **Read for context:** `src/scenes/Game.svelte`, `src/lib/game/player.js`
+
+## Implementation Steps
+1. Add `
` wrapping the `.grid` element in `GameBoard.svelte`.
+2. CSS: `.viewport { max-width: 80vh; max-height: 80vh; overflow: auto; scroll-behavior: smooth; }`.
+3. Bind `ref` to the player's cell element (use Svelte action or id).
+4. Add `$effect` watching `player.row`, `player.col` → call `scrollIntoView` on the player cell.
+5. Test with temporary 12x12 dummy level (don't commit dummy level).
+6. Verify no regression at 6x6, 8x8.
+7. Run `npm run build` to check compile.
+
+## Todo List
+- [ ] Add viewport wrapper + CSS in `GameBoard.svelte`
+- [ ] Wire player cell ref + `$effect` scrollIntoView
+- [ ] Manual test: 6x6 (no scroll), 12x12 (scroll follows)
+- [ ] `npm run build` passes
+- [ ] `npm run dev` smoke test on L1 + temp 12x12
+
+## Success Criteria
+- 12x12 grid fully playable; camera follows player.
+- 6x6/8x8 grids render identically to before (no scrollbars when grid fits).
+- Smooth scroll animation on each move.
+
+## Risk Assessment
+- **Risk:** `scrollIntoView` with smooth animation may lag on fast moves. **Mitigation:** test with held arrow key; switch to instant scroll if lag visible.
+- **Risk:** Svelte 5 reactivity on ref may not fire. **Mitigation:** use `bind:this` + derived runes or simple DOM query.
+
+## Security Considerations
+N/A — pure UI.
+
+## Next Steps
+Phase 2 (solver) can run in parallel with this phase — no file overlap.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-02-level-solver.md b/plans/260421-2042-level-redesign-solvability/phase-02-level-solver.md
new file mode 100644
index 0000000..a5a1814
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-02-level-solver.md
@@ -0,0 +1,79 @@
+# Phase 2: Level Solver (BFS)
+
+## Context
+- Brainstorm: `plans/reports/brainstorm-260421-2042-level-redesign-solvability.md` §4
+- Existing engine: `src/lib/game/grid-system.js`, `guards.js`, `turn-manager.js`, `player.js`
+
+## Overview
+- **Priority:** CRITICAL — blocks Phase 3 (tests) and all redesigns.
+- **Status:** pending
+- Pure-JS BFS solver that replays the real turn simulation to prove reachability.
+
+## Key Insights
+- **Must import guard AI from `guards.js`** — do NOT reimplement, or solver disagrees with runtime.
+- Chaser is stateful → include positions in state key.
+- Mirror + rotating beams are pure function of rotating direction + mirror positions → not part of state key (recomputed each turn).
+- Cycle length = `LCM(4 [rotating], 2 [blinking], patrol_path_lengths...)`. Typical 4–24.
+- L12 expanding-wave mechanic lives in `princess-mechanic.js` — solver must hook in `waveRadius` as state dim.
+
+## Requirements
+- Function `solveLevel(levelDef, { maxStates = 10_000_000 } = {})` → `{ solvable: boolean, path?: string[], states_explored: number }`.
+- `path` is array of actions: `'up' | 'down' | 'left' | 'right' | 'wait'`.
+- BFS guarantees shortest path.
+- Hard cap on state count; returns `solvable: false, reason: 'budget_exhausted'` if exceeded.
+- Returns `solvable: false, reason: 'no_path'` if BFS completes without reaching goal.
+
+## Architecture
+
+State key:
+```
+key = `${pRow},${pCow}|${chaserPositions.map(c => c.row+','+c.col).join(';')}|${turn % cycle}|${waveRadius ?? 0}`
+```
+
+Per step:
+1. Apply action (move or wait); validate against walls + bounds.
+2. Run `turnManager.nextTurn()` equivalent on a cloned grid + guards.
+3. If player cell is lit after guard update → prune.
+4. If player at goal → return path.
+5. Enqueue new state if unseen.
+
+## Related Files
+- **Create:** `src/lib/game/level-solver.js` (main BFS + state cloning helpers)
+- **Create:** `src/lib/game/level-solver.test.js` (unit tests for solver itself)
+- **Read for context:** `src/lib/game/guards.js`, `turn-manager.js`, `grid-system.js`, `princess-mechanic.js`
+
+## Implementation Steps
+1. Extract or reuse guard instantiation from `level-manager.js` so solver can build guards from a level def.
+2. Write `cloneState(grid, guards, player)` — deep copies cells, guard positions, player.
+3. Write `stateKey(player, guards, turn, cycle, waveRadius)`.
+4. Write `computeCycle(level)` → LCM of per-guard periods.
+5. Implement BFS loop with visited Set and state budget.
+6. Hook expanding-wave: include `waveRadius` in state when level has `isFinalLevel` + princess mechanic.
+7. Return `{ solvable, path, states_explored, par }`.
+
+## Todo List
+- [ ] Create `level-solver.js` scaffolding + exports
+- [ ] Implement state cloning helpers
+- [ ] Implement `computeCycle`
+- [ ] Implement BFS core loop with state budget
+- [ ] Wire chaser positions into state key
+- [ ] Wire princess-mechanic `waveRadius` into state key
+- [ ] Unit tests: toy 3x3 level (trivial solve), 4x4 blocked level (no path), cycle computation
+- [ ] `npm test` passes
+- [ ] Solver finishes L1 in <5s; L11 in <60s on dev machine
+
+## Success Criteria
+- Solver correctly solves L1 (current) returning a path ≤ par.
+- Solver returns `solvable: false` for current L2 (matches known bug).
+- Solver returns `solvable: false` for current L12 (preserves invariant).
+- Unit tests pass.
+
+## Risk Assessment
+- **Risk:** State explosion on 12x12 + 2 chasers. **Mitigation:** profile; if >10M states, tighten cycle (shorter patrol paths) or add heuristic dedup (e.g., ignore chaser when >N cells away).
+- **Risk:** Chaser simulation non-deterministic (random tiebreak in pathfinding). **Mitigation:** ensure `guards.js` chaser AI is deterministic; if not, make it so (sorted tiebreak).
+
+## Security Considerations
+N/A — pure compute in test harness.
+
+## Next Steps
+Phase 3 writes the Vitest suite using this solver.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-03-solvability-tests.md b/plans/260421-2042-level-redesign-solvability/phase-03-solvability-tests.md
new file mode 100644
index 0000000..bd6eed0
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-03-solvability-tests.md
@@ -0,0 +1,53 @@
+# Phase 3: Solvability Test Suite + CI Wiring
+
+## Context
+- Brainstorm §4.2
+- Depends on: Phase 2 (solver)
+
+## Overview
+- **Priority:** HIGH — gate for all redesign phases.
+- **Status:** pending
+- Vitest suite that asserts invariants on every PR touching `levels.js`.
+
+## Requirements
+- For L1, L3–L11: assert `solveLevel(level).solvable === true`.
+- For L2: known fail (baseline); test marked `.skip` with TODO to unskip after Phase 4.
+- For L12: assert `solveLevel(level).solvable === false` (unsolvable invariant).
+- Each passing level also asserts `par = path.length + 2` (or logs mismatch for manual par tuning).
+
+## Architecture
+- One describe block per level; `test.each(LEVELS)` pattern.
+- Long-running: set Vitest timeout to 120s per test.
+- Separate suite file so dev can skip fast unit tests vs full solvability run.
+
+## Related Files
+- **Create:** `src/lib/levels/levels.solvability.test.js`
+- **Modify:** `package.json` — add script `"test:solvability": "vitest run src/lib/levels/levels.solvability.test.js"`
+- **Modify:** `.github/workflows/*.yml` if CI exists (else note for later)
+
+## Implementation Steps
+1. Create test file, import `LEVELS` and `solveLevel`.
+2. Loop levels, assert expected solvability per hard rules.
+3. Add par validation (warn only, don't fail yet — par tuning happens in redesign phases).
+4. Add `test.each` table for grid size validation (L1-2 = 8x8, L3-4 = 9x9, etc.) — initially all fail; phase-by-phase unskips.
+5. Add npm script.
+6. If `.github/workflows` exists, add step: `npm run test:solvability`.
+
+## Todo List
+- [ ] Write solvability test file
+- [ ] Skip L2 initially with TODO
+- [ ] Add par-delta warning (console.warn, not fail)
+- [ ] Add grid-size table (initially all skipped; unskip per phase)
+- [ ] Add npm script
+- [ ] CI integration (if CI present)
+- [ ] Confirm local: L1/3-11/12 assertions pass
+
+## Success Criteria
+- `npm run test:solvability` passes with L2 skipped and L12 asserted-unsolvable.
+- CI (if present) runs solvability on PRs.
+
+## Risk Assessment
+- **Risk:** Solver too slow for CI. **Mitigation:** run solvability only on PRs that touch `levels.js` or `level-solver.js` (path-filter in CI).
+
+## Next Steps
+Phase 4 redesigns L1-L2. After L2 passes, unskip its assertion here.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-04-redesign-act1-l1-l2.md b/plans/260421-2042-level-redesign-solvability/phase-04-redesign-act1-l1-l2.md
new file mode 100644
index 0000000..597daa9
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-04-redesign-act1-l1-l2.md
@@ -0,0 +1,46 @@
+# Phase 4: Act 1 Redesign (L1–L2, 8x8)
+
+## Context
+- Brainstorm §3.1, §6 (migration)
+- Depends on: Phase 3 (tests)
+
+## Overview
+- **Priority:** HIGH (fixes L2 unsolvability bug).
+- **Status:** pending
+- Act 1 teaches movement + static guards.
+
+## Requirements
+- Both levels: 8x8 grid, preserved `id`/`name`/`storyKey`.
+- **L1 "Garden Path":** 0 guards (gentle intro, pure movement/walls); par ~12.
+- **L2 "The Watchtower":** 3 static guards, ≥2 viable paths; par ~16.
+- L2 must be SOLVABLE this time.
+
+## Implementation Steps
+1. Sketch L1 8x8 grid: start (0,0), goal (7,7), ~10 walls forming winding but connected path.
+2. Run solver on L1 → assert `solvable`; set par = path.length + 2.
+3. Sketch L2 8x8 grid: 3 static guards placed so ≥2 distinct paths exist; walls widen the arena.
+4. Run solver on L2 → assert solvable; set par = path.length + 2.
+5. Update `src/lib/levels/levels.js` — replace L1, L2 entries in place.
+6. Unskip L2 assertion in `levels.solvability.test.js`.
+7. Run `npm run test:solvability` → both pass.
+8. Manual playthrough via `npm run dev`.
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L1, L2 entries only)
+- **Modify:** `src/lib/levels/levels.solvability.test.js` (unskip L2, enable L1-2 grid-size check)
+
+## Todo List
+- [ ] Draft L1 layout, verify with solver
+- [ ] Draft L2 layout with 3 guards + 2 paths, verify with solver
+- [ ] Replace entries in `levels.js`
+- [ ] Unskip L2 test
+- [ ] Solvability suite green
+- [ ] Manual playthrough L1 + L2 via `npm run dev`
+
+## Success Criteria
+- L1 and L2 solvable per solver.
+- L2 has ≥2 distinct solutions (verify by running solver with different seeds or via manual BFS count).
+- Par values match `solver_optimal + 2`.
+
+## Risks
+- **Overlapping wall+light** (original L2 bug). **Mitigation:** lint via assertion in solver — no wall cell should appear in any guard's `litCells` array.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-05-redesign-act2-l3-l4.md b/plans/260421-2042-level-redesign-solvability/phase-05-redesign-act2-l3-l4.md
new file mode 100644
index 0000000..a132eef
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-05-redesign-act2-l3-l4.md
@@ -0,0 +1,41 @@
+# Phase 5: Act 2 Redesign (L3–L4, 9x9)
+
+## Context
+- Brainstorm §3.1
+- Depends on: Phase 4 (Act 1 done; solver + tests trusted)
+
+## Overview
+- **Priority:** MEDIUM
+- **Status:** pending
+- Act 2 introduces **rotating** + **blinking** (blinking promoted from old Act 3).
+
+## Requirements
+- Both levels: 9x9 grid, preserved metadata.
+- **L3 "Vegetable Patrol":** 4 guards total — mix of static + rotating (≥1 rotating); par ~18.
+- **L4 "The Searchlight":** 5 guards — static + rotating + 1 blinking; timing window required; par ~22.
+
+## Implementation Steps
+1. Draft L3 9x9 with 4 guards; verify solver solvable + ≥2 paths.
+2. Draft L4 9x9 with 5 guards including blinking (introduce teaching level); verify solvable; ensure `wait` action is useful (solver path includes at least one `wait`).
+3. Replace L3, L4 in `levels.js`.
+4. Enable L3-4 grid-size assertion in test.
+5. `npm run test:solvability` green.
+6. Manual playthrough.
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L3, L4)
+- **Modify:** `src/lib/levels/levels.solvability.test.js`
+
+## Todo List
+- [ ] L3 drafted + solver-verified
+- [ ] L4 drafted + solver-verified (blinking intro)
+- [ ] Entries replaced
+- [ ] Tests green
+- [ ] Manual playthrough
+
+## Success Criteria
+- Solver proves both solvable with pars reasonable.
+- L4 solution requires a `wait` action (teaches mechanic).
+
+## Risks
+- **Rotating guard cycle + blinking cycle interaction** may create tight windows. **Mitigation:** solver par check; if par >30, simplify.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-06-redesign-act3-l5-l6.md b/plans/260421-2042-level-redesign-solvability/phase-06-redesign-act3-l5-l6.md
new file mode 100644
index 0000000..5b95d13
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-06-redesign-act3-l5-l6.md
@@ -0,0 +1,41 @@
+# Phase 6: Act 3 Redesign (L5–L6, 10x10)
+
+## Context
+- Brainstorm §3.1
+- Depends on: Phase 1 (viewport for 10x10), Phase 5
+
+## Overview
+- **Priority:** MEDIUM
+- **Status:** pending
+- Act 3 introduces **patrolling** (promoted from old Act 4).
+
+## Requirements
+- Both levels: 10x10 grid, preserved metadata.
+- **L5 "Fortress Gate":** 6 guards — static + rotating + blinking + 1 patrolling (intro); par ~22.
+- **L6 "The Flickering Corridor":** 7 guards — mix, ≥2 patrollers OR 1 patroller with longer path; par ~28.
+
+## Implementation Steps
+1. Draft L5 10x10 with 6 guards (1 patrolling); solver-verify.
+2. Draft L6 10x10 with 7 guards; solver-verify.
+3. Replace in `levels.js`.
+4. Enable 10x10 grid-size checks.
+5. Test green.
+6. Manual playthrough — verify viewport scrolls correctly (phase-1 integration smoke).
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L5, L6)
+- **Modify:** `src/lib/levels/levels.solvability.test.js`
+
+## Todo List
+- [ ] L5 drafted + verified (patrolling intro)
+- [ ] L6 drafted + verified
+- [ ] Entries replaced
+- [ ] Tests green
+- [ ] Manual playthrough confirms viewport scroll works
+
+## Success Criteria
+- Both solvable; ≥2 paths per level.
+- Patroller path length ≥4 cells (meaningful patrol).
+
+## Risks
+- **Patrol path length inflates cycle** → solver state blowup. **Mitigation:** cap patrol path ≤6 cells in Act 3.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-07-redesign-act4-l7-l8.md b/plans/260421-2042-level-redesign-solvability/phase-07-redesign-act4-l7-l8.md
new file mode 100644
index 0000000..5294e7d
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-07-redesign-act4-l7-l8.md
@@ -0,0 +1,41 @@
+# Phase 7: Act 4 Redesign (L7–L8, 11x11)
+
+## Context
+- Brainstorm §3.1
+- Depends on: Phase 1 (viewport), Phase 6
+
+## Overview
+- **Priority:** MEDIUM
+- **Status:** pending
+- Act 4 introduces **mirror** guards (promoted from old Act 5).
+
+## Requirements
+- Both levels: 11x11 grid, preserved metadata.
+- **L7 "The Underground Passage":** 7 guards — mix + 1 mirror (intro); par ~26.
+- **L8 "The Gauntlet":** 8 guards — ≥2 mirrors + ≥2 patrolling + rotating; par ~32.
+
+## Implementation Steps
+1. Draft L7 11x11 with mirror intro; solver-verify (requires solver to correctly model mirror deflection).
+2. Draft L8 11x11 with complex mirror setup; solver-verify.
+3. Replace in `levels.js`.
+4. Tests green.
+5. Manual playthrough.
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L7, L8)
+- **Modify:** `src/lib/levels/levels.solvability.test.js`
+
+## Todo List
+- [ ] L7 drafted (mirror intro) + verified
+- [ ] L8 drafted + verified
+- [ ] Entries replaced
+- [ ] Tests green
+- [ ] Manual playthrough
+
+## Success Criteria
+- Both solvable; mirror deflection creates a hazard that player must account for.
+- L8 has ≥3 guards of same dynamic type (patrol/rotating) — "gauntlet" flavor.
+
+## Risks
+- **Solver mirror logic mismatch with runtime.** **Mitigation:** Phase 2 solver imports guards.js — single source of truth.
+- **11x11 + mirrors + many guards → state blowup.** **Mitigation:** profile; may need state cap bump or heuristic pruning.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-08-redesign-act5-l9-l10.md b/plans/260421-2042-level-redesign-solvability/phase-08-redesign-act5-l9-l10.md
new file mode 100644
index 0000000..081d596
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-08-redesign-act5-l9-l10.md
@@ -0,0 +1,40 @@
+# Phase 8: Act 5 Redesign (L9–L10, 12x12)
+
+## Context
+- Brainstorm §3.1
+- Depends on: Phase 1 (viewport), Phase 7
+
+## Overview
+- **Priority:** MEDIUM
+- **Status:** pending
+- Act 5 introduces **chaser** guards.
+
+## Requirements
+- Both levels: 12x12 grid, preserved metadata.
+- **L9 "The Decoy Path":** 8 guards — mix + 1 chaser (intro); par ~34.
+- **L10 "Hall of Mirrors":** 9 guards — chaser + multiple mirrors + rotating; par ~38.
+
+## Implementation Steps
+1. Draft L9 12x12 with chaser intro; solver-verify (longest run; may take minutes).
+2. Draft L10 12x12 with mirror-heavy layout + chaser; solver-verify.
+3. Replace in `levels.js`.
+4. Tests green.
+5. Manual playthrough — verify viewport + camera follow smooth at 12x12.
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L9, L10)
+- **Modify:** `src/lib/levels/levels.solvability.test.js`
+
+## Todo List
+- [ ] L9 drafted (chaser intro) + verified
+- [ ] L10 drafted + verified
+- [ ] Entries replaced
+- [ ] Tests green
+- [ ] Manual playthrough at 12x12
+
+## Success Criteria
+- Both solvable; chaser creates genuine threat (solver proves shortest path avoids chaser).
+- Solver completes within 10min per level (or raise state cap).
+
+## Risks
+- **Solver budget exhausted on 12x12 + chaser + long cycle.** **Mitigation:** shorten patrol paths; cap chaser detection radius at 3.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-09-redesign-l11.md b/plans/260421-2042-level-redesign-solvability/phase-09-redesign-l11.md
new file mode 100644
index 0000000..024b123
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-09-redesign-l11.md
@@ -0,0 +1,42 @@
+# Phase 9: L11 Redesign — Throne Room (12x12)
+
+## Context
+- Brainstorm §3.1
+- Depends on: Phase 1 (viewport), Phase 8
+
+## Overview
+- **Priority:** MEDIUM
+- **Status:** pending
+- L11 is the climax before the impossible finale. All guard types combined.
+
+## Requirements
+- 12x12 grid, preserved metadata (`id: 11`, `name: "The Throne Room"`, `storyKey: level11Story`).
+- 9 guards: ≥1 of each type (static, rotating, blinking, patrolling, mirror, chaser).
+- Must be solvable but hard (par ~42).
+- Multiple paths; each path tests a different mix of skills learned.
+
+## Implementation Steps
+1. Draft 12x12 with all 6 guard types represented; solver-verify.
+2. Ensure par > L10 par (difficulty climb).
+3. Replace L11 in `levels.js`.
+4. Tests green.
+5. Manual playthrough — note difficulty feel; adjust if solver par drastically exceeds 42.
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L11)
+- **Modify:** `src/lib/levels/levels.solvability.test.js`
+
+## Todo List
+- [ ] L11 drafted with all 6 guard types
+- [ ] Solver-verified
+- [ ] Par sensible (≤~50)
+- [ ] Tests green
+- [ ] Manual playthrough
+
+## Success Criteria
+- Solvable.
+- Uses every guard type at least once.
+- Feels like a climactic combo of all mechanics.
+
+## Risks
+- **Mirror + chaser + rotating interaction too chaotic.** **Mitigation:** if solver par >50 or feels unfair, reduce to 8 guards (drop one redundant type).
diff --git a/plans/260421-2042-level-redesign-solvability/phase-10-redesign-l12-easter-egg.md b/plans/260421-2042-level-redesign-solvability/phase-10-redesign-l12-easter-egg.md
new file mode 100644
index 0000000..dc05108
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-10-redesign-l12-easter-egg.md
@@ -0,0 +1,66 @@
+# Phase 10: L12 Redesign — Princess Chamber + Console Easter Egg (13x13)
+
+## Context
+- Brainstorm §6, §10
+- Memory: `project_level12_unsolvable.md`
+- Depends on: Phase 9
+
+## Overview
+- **Priority:** HIGH (preserves core narrative invariant).
+- **Status:** pending
+- L12 MUST remain unsolvable by normal play. Only console-based teleport can trigger the win screen.
+
+## Hard Invariant
+Solver MUST return `solvable: false` for L12 after redesign. Test asserts this; CI blocks any change that makes L12 solvable.
+
+## Requirements
+- 13x13 grid (biggest), preserved metadata (`id: 12`, `isFinalLevel: true`).
+- 10 guards + expanding-wave mechanic from `princess-mechanic.js`.
+- Console easter-egg hook exposed on `window` so dev-tools users can teleport to goal.
+- Normal win flow triggers regardless of how goal was reached.
+
+## Architecture
+- **Guards:** all 6 types represented, packed aggressively around goal.
+- **Expanding wave:** intact from current impl — lights ring around goal after player within distance 4.
+- **Console hook:** expose `window.__nntvDev = { teleport(row, col) { ... }, reveal() { ... } }`.
+ - `teleport` mutates the live game state via existing reactive store.
+ - Documented in code comments only; NOT in README, NOT in guide UI.
+- **Win flow unchanged:** existing `isAtGoal()` check + `LevelCompletePopup.svelte` fire normally when player position = goal.
+
+## Implementation Steps
+1. Draft 13x13 layout with 10 guards + wave mechanic.
+2. Solver-verify `solvable: false` within 10M state budget.
+3. Replace L12 in `levels.js`.
+4. Add `window.__nntvDev` hook in `src/main.js` (or `Game.svelte`) — gated behind Svelte store reference.
+5. Manual test:
+ - Normal play → impossible confirmed.
+ - `window.__nntvDev.teleport(goal.row, goal.col)` in console → win flow triggers.
+6. Add test in `levels.solvability.test.js`: assert L12 unsolvable.
+7. Manual playthrough.
+
+## Related Files
+- **Modify:** `src/lib/levels/levels.js` (L12)
+- **Modify:** `src/main.js` or `src/scenes/Game.svelte` (expose dev hook)
+- **Modify:** `src/lib/levels/levels.solvability.test.js`
+- **Read:** `src/lib/game/princess-mechanic.js`
+
+## Todo List
+- [ ] L12 drafted (13x13, 10 guards + wave)
+- [ ] Solver confirms unsolvable
+- [ ] Dev hook (`window.__nntvDev.teleport`) wired
+- [ ] Manual: normal play impossible, console win works
+- [ ] Solvability test asserts `!solvable`
+- [ ] Code comment documents easter egg
+
+## Success Criteria
+- Solver asserts `solvable: false` — test green.
+- Pressing every direction + wait indefinitely never reaches goal (manual spot-check).
+- `window.__nntvDev.teleport(12, 12)` from console → LevelCompletePopup fires.
+- No in-game UI/readme hint about the easter egg.
+
+## Risks
+- **Dev hook security.** **Mitigation:** non-sensitive (just game state); no auth/data exposure. Fine for a client-only game.
+- **Wave mechanic solvability.** **Mitigation:** solver includes `waveRadius` in state key (Phase 2).
+
+## Security Considerations
+- `window.__nntvDev` is intentionally world-accessible in-browser. Acceptable — no server, no user data.
diff --git a/plans/260421-2042-level-redesign-solvability/phase-11-docs-and-smoke-test.md b/plans/260421-2042-level-redesign-solvability/phase-11-docs-and-smoke-test.md
new file mode 100644
index 0000000..b8fb901
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/phase-11-docs-and-smoke-test.md
@@ -0,0 +1,53 @@
+# Phase 11: Docs + Full Smoke Test
+
+## Context
+- Brainstorm §11
+- Depends on: all prior phases
+
+## Overview
+- **Priority:** LOW–MEDIUM (final polish, CI gate).
+- **Status:** pending
+- Update `docs/`, verify full playthrough, tune pars if needed.
+
+## Requirements
+- Update `docs/system-architecture.md` — add solver + viewport sections.
+- Update `docs/code-standards.md` — note "all level edits must run solvability suite".
+- README unchanged re: easter egg (keep hidden).
+- Full manual playthrough L1→L11; verify L12 is impossible but console-teleport wins.
+
+## Implementation Steps
+1. Run full test suite: `npm test && npm run test:solvability`.
+2. Check `docs/` files exist — if not, create.
+3. Append solver architecture section to `docs/system-architecture.md`.
+4. Append level-authoring rule to `docs/code-standards.md`: "never commit `levels.js` without running `npm run test:solvability`".
+5. Add codebase-summary.md entry for `level-solver.js`.
+6. Manual playthrough L1 → L11 (screenshot key moments for regression visibility).
+7. Par tuning pass: if solver_par + 2 feels too loose/tight per actual play, adjust.
+8. `npm run build` produces clean dist.
+
+## Related Files
+- **Modify:** `docs/system-architecture.md`
+- **Modify:** `docs/code-standards.md`
+- **Modify:** `docs/codebase-summary.md` (if exists)
+- **Modify (maybe):** `src/lib/levels/levels.js` (par tuning)
+
+## Todo List
+- [ ] Full test suite green
+- [ ] docs/system-architecture.md updated
+- [ ] docs/code-standards.md updated
+- [ ] Manual playthrough L1→L11 complete, no blockers
+- [ ] L12 console win confirmed
+- [ ] Par tuning (if any) complete
+- [ ] `npm run build` clean
+
+## Success Criteria
+- All 11 solvable levels playable end-to-end without test failures.
+- L12 impossible by normal play; console win works.
+- Docs reflect new solver + viewport architecture.
+
+## Risks
+- **Par values feel off in real play.** **Mitigation:** dedicated tuning pass here; adjust before merge.
+
+## Next Steps
+- Archive plan via `/ck:plan archive`.
+- Merge to main, tag release if versioned.
diff --git a/plans/260421-2042-level-redesign-solvability/plan.md b/plans/260421-2042-level-redesign-solvability/plan.md
new file mode 100644
index 0000000..97fbd89
--- /dev/null
+++ b/plans/260421-2042-level-redesign-solvability/plan.md
@@ -0,0 +1,61 @@
+---
+name: Level Redesign & Solvability
+slug: level-redesign-solvability
+created: 2026-04-21
+status: pending
+blockedBy: []
+blocks: []
+source: plans/reports/brainstorm-260421-2042-level-redesign-solvability.md
+---
+
+# Level Redesign & Solvability — Implementation Plan
+
+Redesign all 12 levels of Night Ninja: Twilight Voyage. Fix L2 unsolvability. Widen grids (8x8→13x13). Add more guards per act. Introduce automated BFS solver + CI-enforced solvability tests. Preserve L12 as unsolvable (console easter-egg win only).
+
+## Hard Rules
+
+1. Exactly 12 levels, 6 acts. No adds/removes/renumbers.
+2. L12 must remain unsolvable by normal play.
+3. Desktop-first. Large grids via scrollable viewport that follows player.
+4. Preserve `id`, `name`, `storyKey`, `isFinalLevel` per level (no progress migration).
+
+## Progression Target
+
+| Act | Levels | Grid | Guard types (cumulative) | Guard count |
+|-----|--------|------|--------------------------|-------------|
+| 1 | 1–2 | 8x8 | static | 0 / 3 |
+| 2 | 3–4 | 9x9 | + rotating + blinking | 4 / 5 |
+| 3 | 5–6 | 10x10 | + patrolling | 6 / 7 |
+| 4 | 7–8 | 11x11 | + mirror | 7 / 8 |
+| 5 | 9–10 | 12x12 | + chaser | 8 / 9 |
+| 6 | 11 | 12x12 | all | 9 |
+| 6 | 12 | 13x13 | all + expanding wave | 10 (unsolvable) |
+
+## Phases
+
+| # | File | Status | Description |
+|---|------|--------|-------------|
+| 1 | [phase-01-scrollable-viewport.md](phase-01-scrollable-viewport.md) | pending | Camera-follow viewport for grids >9x9 |
+| 2 | [phase-02-level-solver.md](phase-02-level-solver.md) | pending | BFS solver in `src/lib/game/level-solver.js` |
+| 3 | [phase-03-solvability-tests.md](phase-03-solvability-tests.md) | pending | Vitest suite + CI wiring; assert L2 fails as baseline |
+| 4 | [phase-04-redesign-act1-l1-l2.md](phase-04-redesign-act1-l1-l2.md) | pending | Act 1 (L1–L2, 8x8, static) |
+| 5 | [phase-05-redesign-act2-l3-l4.md](phase-05-redesign-act2-l3-l4.md) | pending | Act 2 (L3–L4, 9x9, + rotating + blinking) |
+| 6 | [phase-06-redesign-act3-l5-l6.md](phase-06-redesign-act3-l5-l6.md) | pending | Act 3 (L5–L6, 10x10, + patrolling) |
+| 7 | [phase-07-redesign-act4-l7-l8.md](phase-07-redesign-act4-l7-l8.md) | pending | Act 4 (L7–L8, 11x11, + mirror) |
+| 8 | [phase-08-redesign-act5-l9-l10.md](phase-08-redesign-act5-l9-l10.md) | pending | Act 5 (L9–L10, 12x12, + chaser) |
+| 9 | [phase-09-redesign-l11.md](phase-09-redesign-l11.md) | pending | L11 Throne Room (12x12, all guards) |
+| 10 | [phase-10-redesign-l12-easter-egg.md](phase-10-redesign-l12-easter-egg.md) | pending | L12 Princess Chamber (13x13, unsolvable) + console win hook |
+| 11 | [phase-11-docs-and-smoke-test.md](phase-11-docs-and-smoke-test.md) | pending | Update docs/ + manual playthrough QA |
+
+## Dependencies
+
+- Phase 1 (viewport) blocks Phases 6–10 (grids ≥10x10 unplayable without it).
+- Phase 2 (solver) blocks Phase 3 (tests need solver).
+- Phase 3 (tests) blocks Phases 4–10 (each redesign validated by tests).
+- Phases 4–9 independent of each other but best done in order (progression curve).
+- Phase 10 depends on Phase 9 (L11 finalized before L12).
+- Phase 11 depends on all previous.
+
+## Source
+
+Brainstorm summary: `plans/reports/brainstorm-260421-2042-level-redesign-solvability.md`
diff --git a/plans/reports/brainstorm-260421-2042-level-redesign-solvability.md b/plans/reports/brainstorm-260421-2042-level-redesign-solvability.md
new file mode 100644
index 0000000..fc0c385
--- /dev/null
+++ b/plans/reports/brainstorm-260421-2042-level-redesign-solvability.md
@@ -0,0 +1,145 @@
+# Level Redesign & Solvability — Brainstorm Summary
+
+**Date:** 2026-04-21
+**Scope:** Redesign all 12 levels of Night Ninja: Twilight Voyage. Fix L2 unsolvability. Expand grid sizes. Add more guards. Preserve L12 as intentionally unsolvable.
+**Status:** Design approved — ready for implementation plan.
+
+---
+
+## 1. Problem Statement
+
+- **L2 unsolvable** — BFS from `(0,0)` cannot reach `(5,5)`. Right-side component `{(3,5),(4,4),(4,5),(5,3),(5,4),(5,5)}` is disconnected by permanent light walls from static guards at `(2,4)` and `(4,2)`.
+- **No solvability gate** — levels authored manually; no automated proof. Risk of regressions on future edits.
+- **Levels feel narrow** — current grids 6x6–9x9 have single corridors; user wants wider, more complex puzzles.
+- **Progression imbalanced** — mirror/chaser/patrolling introduced too late (Acts 4–5); early acts feel sparse.
+
+## 2. Hard Rules (invariants)
+
+1. **Exactly 12 levels, 6 acts.** No adds, removes, renumbers.
+2. **L12 must remain unsolvable by normal play.** Only console easter-egg win (user teleports player to goal via dev tools). Win condition / congratulations screen still fires when goal reached by any means.
+3. **Desktop-first.** No mobile responsive work. Large grids handled via scrollable viewport that follows the player.
+4. **Preserve** `id`, `name`, `storyKey`, `isFinalLevel` per level — zero progress-persistence migration.
+
+## 3. Solution — Redesign Philosophy
+
+### 3.1 Progression (rebalanced acts)
+
+| Act | Levels | Grid | Guard types (cumulative) | Guard count |
+|-----|--------|------|--------------------------|-------------|
+| 1 | 1–2 | 8x8 | static | 0 / 3 |
+| 2 | 3–4 | 9x9 | + rotating + **blinking** (promoted from Act 3) | 4 / 5 |
+| 3 | 5–6 | 10x10 | + **patrolling** (promoted from Act 4) | 6 / 7 |
+| 4 | 7–8 | 11x11 | + **mirror** (promoted from Act 5) | 7 / 8 |
+| 5 | 9–10 | 12x12 | + chaser | 8 / 9 |
+| 6 | 11 | 12x12 | all types combined | 9 |
+| 6 | 12 | 13x13 | all types + expanding-wave mechanic | 10 (unsolvable) |
+
+### 3.2 Design Rules Per Level
+
+- **Connectivity gate:** at least one reachable path from start→goal in solver (except L12).
+- **Widen, don't narrow:** add 2–3 alternative routes so guards force *decisions*, not single chokepoints.
+- **More guards ≠ more walls.** Keep wall density ~20–25% of grid area; increase guard count per act tier.
+- **Par = solver_optimal + 2** (small slack for undo experimentation).
+- **No redundant wall-on-light** (L2 had wall `(3,4)` over guard light `(3,4)` — sloppy overlap, remove).
+- **Guard beam pruning:** rotating beams stop at walls — use walls strategically to create safe windows.
+
+## 4. Solvability Solver (automated validation)
+
+### 4.1 State & Search
+
+**State key:** `(player_row, player_col, chaser_positions_tuple, turn_mod_cycle)`
+- `cycle = LCM(4 [rotating], 2 [blinking], patrol_path_length_i...)`. Typical cycles 4–24.
+- **Chaser** is stateful — include each chaser's position in state key. State space stays tractable (<10M for 12x12 + 2 chasers + cycle 24).
+- **Mirror beams** are pure function of rotating-guard directions + mirror positions each turn — not part of state.
+
+**Transitions:** 5 actions per turn — `up / down / left / right / wait`.
+**Pruning:** after applying action + running guard update for next turn, if player cell `isLight` → dead branch.
+**Algorithm:** BFS (guarantees shortest). Bail out at state-count cap (~10M) with "unsolvable within budget" → manual check.
+
+### 4.2 Location & Modules
+
+- `src/lib/game/level-solver.js` — pure BFS solver, reuses existing `grid-system.js`, `guards.js`, `turn-manager.js`.
+- `src/lib/levels/levels.solvability.test.js` — Vitest suite asserting:
+ - For L1–L11: solver returns a valid path.
+ - For L12: solver returns no path within budget (unsolvable invariant).
+ - Par values match `solver_optimal + 2`.
+- CI hook: run solvability suite on every PR that touches `levels.js`.
+
+### 4.3 Known Limitations
+
+- Chaser path depends on guard AI implementation — solver must use *identical* chaser logic as runtime (import from `guards.js`, don't reimplement).
+- Cycle computation must account for patrol path length (may be odd / prime).
+- L12's expanding-wave mechanic needs the solver to model princess-mechanic.js state — include `waveRadius` in state key.
+
+## 5. Scrollable Viewport (prereq for large grids)
+
+- Current rendering assumes full grid fits on screen.
+- Need: camera-follow the player so 12x12 / 13x13 grids are playable on 1080p screens.
+- Implementation touch points:
+ - `src/components/GameBoard.svelte` (or equivalent) — CSS `overflow: auto` + `scroll-behavior: smooth` + programmatic `scrollIntoView` on player position change.
+ - Viewport: show ~9x9 cells visible; pan on player move.
+- Out-of-scope for this redesign brainstorm but **blocker** for Acts 4–6 playability.
+
+## 6. L12 Easter Egg — Console Win
+
+- Expose a minimal dev hook on `window` (e.g., `window.__nntvDev = { teleportToGoal() { ... } }`) OR rely on existing game-state reactivity so user can directly mutate player position via devtools.
+- Reaching goal by any means → normal win flow (congratulations, progress saved).
+- Document the easter egg ONLY in code comments, not in-game UI or README.
+
+## 7. Migration & Rollout
+
+- **In-place rewrite** of `src/lib/levels/levels.js`. No new file.
+- Preserve each level's `id`, `name`, `storyKey`, `isFinalLevel`, array position. No changes to `src/lib/localization/*` story keys needed.
+- Update `parMoves` from solver output for all levels.
+- Tests first: land solver + solvability suite **before** level content changes, so each new level design is validated as it's written.
+
+## 8. Evaluated Alternatives
+
+| Approach | Pros | Cons | Verdict |
+|----------|------|------|---------|
+| Fix L2 only, leave others | Minimal churn | Doesn't address "narrow levels" complaint; no regression protection | **Rejected** |
+| Keep grids, add guards only | No viewport work | Crowded; still narrow corridors; low decision value | **Rejected** |
+| Aggressive grid expansion + more guards + solver | Solves all issues; prevents future regressions | Requires viewport work; solver effort | **Chosen** |
+| Expand to 15–18 levels | Richer progression | Breaks progress persistence, story keys, count invariant | **Rejected** (violates hard rule) |
+
+## 9. Success Criteria
+
+- [ ] Vitest suite: 11/11 levels solvable, L12 unsolvable. Passes on CI.
+- [ ] Every level has ≥2 distinct BFS-discovered paths OR clear single-path justification in code comment.
+- [ ] All acts visually wider than before (row/col counts match Section 3.1).
+- [ ] Scrollable viewport works on 1080p / 1440p screens for 12x12 grids.
+- [ ] Win condition fires when L12 goal is reached via console manipulation.
+- [ ] No changes to story keys, level IDs, or progress-persistence schema.
+
+## 10. Next Steps (Implementation Plan Phases)
+
+1. **Phase 1:** Implement scrollable viewport (prereq for >9x9 grids).
+2. **Phase 2:** Build `level-solver.js` + unit tests on current L1, L3–L11 (should solve) and L12 (should fail). Accept that L2 will fail — that's the baseline bug.
+3. **Phase 3:** Solvability test suite — wire into CI.
+4. **Phase 4:** Redesign L1–L2 (Act 1, 8x8). Run solver, lock par values.
+5. **Phase 5:** Redesign L3–L4 (Act 2, 9x9, introduce blinking earlier).
+6. **Phase 6:** Redesign L5–L6 (Act 3, 10x10, introduce patrolling).
+7. **Phase 7:** Redesign L7–L8 (Act 4, 11x11, introduce mirror).
+8. **Phase 8:** Redesign L9–L10 (Act 5, 12x12, introduce chaser).
+9. **Phase 9:** Redesign L11 (Act 6, 12x12, all guards combined).
+10. **Phase 10:** Redesign L12 (Act 6, 13x13, preserve unsolvable invariant; expose console win hook).
+11. **Phase 11:** Update `docs/` (code-standards, system-architecture) with solver + easter-egg notes. Smoke-test full playthrough.
+
+## 11. Risks
+
+| Risk | Mitigation |
+|------|------------|
+| Solver too slow on chaser+long-cycle levels | State cap 10M; if exceeded, reduce cycle via shorter patrol paths |
+| Viewport work reveals rendering bugs | Isolate in Phase 1; land before level changes |
+| Solver disagrees with runtime (chaser logic) | Import guard logic from `guards.js` — single source of truth |
+| L12 accidentally solvable after redesign | Solver test asserts unsolvability; fails CI |
+| Parsing console easter egg as "cheating" concern | Document in code comments only; feature is intentional |
+
+---
+
+## Unresolved Questions
+
+1. **Console easter-egg API shape** — expose `window.__nntvDev.teleport()` explicitly, or rely on Svelte 5 reactive store mutation? Simplest path TBD in Phase 10.
+2. **Viewport implementation owner** — is scrollable viewport a separate brainstorm/plan, or bundled into this redesign's Phase 1?
+3. **Story text updates** — do any `storyKey` copy lines need rewriting to match new grid sizes/themes (e.g., "narrow corridor" lines on now-wider levels)? Decision deferred to QA after redesigns land.
+4. **Chaser count in Act 5+** — cap at 1 per level, or allow 2 in L10/L11? Affects solver state-space budget.