diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 26da8e3..579417b 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -57,9 +57,12 @@ Plans:
5. A Playwright e2e smoke test passes: it loads the game, dismisses the begin gate, plants a seed, fast-forwards growth, harvests a fragment, verifies the fragment text appears in the journal, refreshes the page, and verifies the harvested fragment persists. Story progression gates on tick count (not wall time), so manipulating the system clock cannot fast-forward through Lura's authored beats.
**Plans:** 5 plans
Plans:
+**Wave 1**
- [x] 02-01-foundations-PLAN.md — BigQty + Zustand 5 store + tick scheduler + V1Payload extension + save lifecycle hooks + Phaser EventBus singleton + ESLint sim-purity rule (Wave 0; foundations every other Phase-2 plan depends on) ✓ 2026-05-09 (12 min) — see 02-01-foundations-SUMMARY.md
- [x] 02-02-begin-plant-grow-PLAN.md — sim/garden core (4×4 grid, 3 plant types, growth state machine, plantSeed) + render layer (Phaser primitives, ready-pulse, tile-coords) + BeginScreen + audio bootstrap + SeedPicker + UI strings (Wave 1; AEST-07, UX-01, GARD-01, GARD-02) ✓ 2026-05-09 (18 min) — see 02-02-begin-plant-grow-SUMMARY.md
- [x] 02-03-harvest-journal-fragments-PLAN.md — Season-1 17 authored fragments + sim/memory selector (deterministic mulberry32, gated, no-dup, sentinel fallback for Pitfall 8) + harvest + compost commands (Pitfall 10 post-commit unlock thresholds) + Memory Journal + FragmentRevealModal + JournalIcon + PIPE-02 structural verifier (Wave 1; GARD-03, GARD-04, MEMR-01..06, PIPE-02) ✓ 2026-05-09 (12 min) — see 02-03-harvest-journal-fragments-SUMMARY.md
+
+**Wave 2** *(blocked on Wave 1 completion)*
- [x] 02-04-lura-gate-beats-PLAN.md — inklecate compile pipeline + 4 authored .ink files (3 Lura beats + compost acknowledgements) + sim/narrative tick-count gate (1st/4th/8th harvest) + LuraDialogue overlay + InkRenderer drip + Phaser gate visual indicator (Wave 2; STRY-01, STRY-06, STRY-07 vacuous, STRY-10) ✓ 2026-05-09 (24 min) — see 02-04-lura-gate-beats-SUMMARY.md
- [x] 02-05-letter-settings-e2e-PLAN.md — sim/offline + auto-harvest + letter Ink + Letter overlay + Settings (Export/Import/Restore) + persistence-toast + boot-path save lifecycle wiring + URL-flag FakeClock injection + Playwright PIPE-07 e2e (Wave 2; UX-02, UX-10, CORE-03, CORE-11, PIPE-07) ✓ 2026-05-09 (20 min) — see 02-05-letter-settings-e2e-SUMMARY.md
**UI hint**: yes
diff --git a/.planning/STATE.md b/.planning/STATE.md
index b68a248..59d6596 100644
--- a/.planning/STATE.md
+++ b/.planning/STATE.md
@@ -2,16 +2,16 @@
gsd_state_version: 1.0
milestone: v1.0
milestone_name: milestone
-status: gaps_found
-stopped_at: "Phase 2 executed end-to-end (5/5 plans, 312/312 vitest, Playwright PIPE-07 green, npm run ci exits 0). gsd-verifier scored 24/24 REQ-IDs structurally PASS and routed 6 tone/live-loop items to HUMAN-UAT.md. Live UAT (user dev-server walkthrough) surfaced 4 first-impression UX gaps that block phase sign-off — these are NOT Phase-3 aesthetic deferrals, they are minimum-viable functional UX gaps: G1 no global page CSS (white halo around dark canvas), G2 no first-run prompt after Begin (player confused — A Dark Room rule violated), G3 dim tile outlines (grid reads as 'gray check block'), G4 isolated gate visual (no surrounding wall context). Gaps documented in 02-VERIFICATION.md frontmatter (status: gaps_found). Phase 3 watercolor + cello deferral preserved — every G-fix uses Phaser primitives or one CSS file, no painted assets. HUMAN-UAT.md tone items (Lura voice, letter cadence) remain pending below the structural gaps and will be re-reviewed after gap closure. Next: /gsd-plan-phase 2 --gaps."
-last_updated: "2026-05-09T15:55:00.000Z"
-last_activity: 2026-05-09
+status: ready_to_execute
+stopped_at: "Phase 2 gap-closure plan (Plan 02-06 uat-gap-closure) created via /gsd-plan-phase 2 --gaps. Single new plan addresses 4 first-impression UX gaps from 2026-05-09 live UAT: G1 (BLOCKING) src/index.css for global page bg #1a1a1a / no white halo; G2 (BLOCKING) FirstRunHint component reading externalized 'Begin where the soil is bare.' from ui-strings.yaml + UiStringsSchema extension (Zod default strip mode would otherwise drop the key) + session-slice firstRunHintDismissed flag; G3 (HIGH) tile-renderer outline brightening 0x4d4d52→0x5a5a60 + hover bump 0x7a7a82; G4 (MEDIUM) gate-renderer wall-band Phaser primitive at gate column with alpha 0.15-0.20. Plan is Wave 0, depends_on [02-01..02-05], 5 tasks (4 gap fixes + e2e integration), 16 files_modified, requirements [GARD-01, AEST-07, UX-01] supplemental coverage. Phase 3 watercolor + cello deferral preserved (zero painted assets, zero new npm deps, V1Payload unchanged). Plan-checker found 1 BLOCKER + 1 WARNING on first pass; planner revised; residual frontmatter + 3 copy refs fixed inline. ROADMAP.md annotated with Wave 1/Wave 2 headers (annotate-dependencies). Next: /gsd-execute-phase 2 to run Plan 02-06; then /gsd-verify-work to flip 02-VERIFICATION.md status gaps_found → verified and re-route to the 6 HUMAN-UAT.md tone items."
+last_updated: "2026-05-09T16:10:00.000Z"
+last_activity: 2026-05-09 -- Phase 2 gap-closure plan created (Plan 02-06)
progress:
total_phases: 8
completed_phases: 1
- total_plans: 12
+ total_plans: 13
completed_plans: 12
- percent: 22
+ percent: 23
---
# Project State
@@ -21,16 +21,16 @@ progress:
See: .planning/PROJECT.md (updated 2026-05-08)
**Core value:** Every idle mechanic must function as a metaphor that the player absorbs without being told. When economy and meaning conflict, meaning wins.
-**Current focus:** Phase 02 — Season 1 Vertical Slice (Soil) — 5/5 plans done + verifier 24/24 PASS, but live UAT found 4 first-impression UX gaps. Awaiting `/gsd-plan-phase 2 --gaps`.
+**Current focus:** Phase 02 — Season 1 Vertical Slice (Soil) — 5/5 plans executed + 24/24 REQ-IDs PASS; gap-closure Plan 02-06 created and ready to execute. Next: `/gsd-execute-phase 2`.
## Current Position
-Phase: 02 (season-1-vertical-slice-soil) — 5/5 plans executed; verifier 24/24 PASS; live UAT found 4 blocking/high gaps; status: gaps_found
-Plans: 5 of 5 created (3 waves); Wave 0 (02-01) DONE; Wave 1 (02-02 + 02-03) DONE; Wave 2 (02-04 + 02-05) DONE; gap-closure plan pending
-Status: All 5 Phase-2 plans executed cleanly (12 atomic feature commits + 5 doc commits). 312/312 vitest green; Playwright PIPE-07 e2e green in 1.6s; npm run ci exits 0. gsd-verifier scored 24/24 REQ-IDs structurally PASS and surfaced 6 inherently subjective tone/live-loop items in HUMAN-UAT.md. User ran the dev server (first live UAT) and reported 4 first-impression UX gaps that the test suite cannot detect: G1 white halo around dark canvas (no global page CSS), G2 no first-run prompt after Begin (A Dark Room rule violated), G3 tile outlines too dim against canvas bg, G4 gate visual stands alone with no wall context. All 4 are minimum-viable functional UX gaps — Phase 3 watercolor deferral preserved (every fix uses Phaser primitives or one CSS file). VERIFICATION.md updated to status: gaps_found with structured gap entries. The HUMAN-UAT.md tone items remain pending below the structural gaps.
-Last activity: 2026-05-09 -- Phase 2 execute complete; live UAT surfaced 4 UX gaps; awaiting /gsd-plan-phase 2 --gaps
+Phase: 02 (season-1-vertical-slice-soil) — 5/5 prior plans executed; verifier 24/24 PASS; gap-closure Plan 02-06 created; status: ready_to_execute
+Plans: 6 of 6 created (Wave 0 + Wave 1 + Wave 2 done; new Wave 0 gap-closure plan pending execution)
+Status: Ready to execute (Plan 02-06)
+Last activity: 2026-05-09 -- Phase 2 gap-closure plan created via /gsd-plan-phase 2 --gaps
-Progress: [██░░░░░░░░] 22%
+Progress: [██░░░░░░░░] 23%
## Verification Results
diff --git a/.planning/phases/02-season-1-vertical-slice-soil/02-06-uat-gap-closure-PLAN.md b/.planning/phases/02-season-1-vertical-slice-soil/02-06-uat-gap-closure-PLAN.md
new file mode 100644
index 0000000..7a6049c
--- /dev/null
+++ b/.planning/phases/02-season-1-vertical-slice-soil/02-06-uat-gap-closure-PLAN.md
@@ -0,0 +1,1461 @@
+---
+phase: 02
+plan: 06
+type: execute
+wave: 0
+depends_on: [02-01, 02-02, 02-03, 02-04, 02-05]
+gap_closure: true
+files_modified:
+ - src/index.css
+ - src/main.tsx
+ - src/index.css.test.ts
+ - src/store/session-slice.ts
+ - src/content/schemas/ui-strings.ts
+ - content/seasons/01-soil/ui-strings.yaml
+ - src/ui/first-run/FirstRunHint.tsx
+ - src/ui/first-run/FirstRunHint.test.tsx
+ - src/ui/first-run/index.ts
+ - src/ui/index.ts
+ - src/App.tsx
+ - src/render/garden/tile-renderer.ts
+ - src/render/garden/tile-renderer.test.ts
+ - src/render/garden/gate-renderer.ts
+ - src/render/garden/gate-renderer.test.ts
+ - tests/e2e/season1-loop.spec.ts
+autonomous: true
+requirements: [GARD-01, AEST-07, UX-01]
+tags: [gap-closure, uat, css, first-run-hint, tile-contrast, gate-context, mvp]
+
+must_haves:
+ truths:
+ - "G1 closed:
has background #1a1a1a + serif color #e8e0d0 + zero margin from frame one — no white halo around the dark canvas (ROADMAP SC1 supplemental — UX-01 lived experience)"
+ - "G2 closed: after BeginScreen dismisses on first run, a single bible-voice line is visible (e.g. 'Begin where the soil is bare.') from `season1.ui_strings.first_run_hint` in ui-strings.yaml — never hardcoded (CLAUDE.md tone constraint, GARD-01 lived experience)"
+ - "G2 closed: FirstRunHint auto-dismisses on the first successful plantSeed dispatch and stays dismissed for the session; reload shows it again until first plant (A-Dark-Room first-run-of-this-tab UX, NOT save-state)"
+ - "G2 closed: `firstRunHintDismissed` lives in src/store/session-slice.ts (NOT in V1Payload — no migrations[2]); resets on hard reload by design"
+ - "G3 closed: empty-tile outline is brighter than 0x4d4d52 (final value ~0x5a5a60) AND the hover state contrasts the resting state (~0x7a7a82 outline + slight fill alpha bump) so the 4×4 grid reads as legible interactive surfaces against #1a1a1a — no painted assets (Phase 3 deferral preserved, GARD-01 lived experience)"
+ - "G4 closed: gate-renderer.ts adds a faint vertical wall band (Phaser primitive — alpha 0.15-0.20 against #1a1a1a) at the gate's column connecting top-to-bottom of the canvas, so the gate reads as part of a wall rather than a floating rectangle (Bible 'walled garden', AEST-07 supplemental coverage)"
+ - "Phase 3 watercolor + cello deferral preserved: every fix uses Phaser primitives or one CSS file. NO painted assets. NO new image loads. NO new npm dependencies."
+ - "Tests landed for all 4 gaps: src/index.css.test.ts (G1), src/ui/first-run/FirstRunHint.test.tsx (G2), src/render/garden/tile-renderer.test.ts (G3), src/render/garden/gate-renderer.test.ts (G4)."
+ - "Playwright tests/e2e/season1-loop.spec.ts extended with two assertions: (a) document.body computed style backgroundColor is rgb(26, 26, 26) after navigation, (b) the FirstRunHint line is visible after Begin dismiss, (c) the FirstRunHint is gone after the first plantSeed dispatches."
+ - "After execution: gsd-verifier handoff is unblocked. The 4 gaps in 02-VERIFICATION.md frontmatter `gaps:` block clear (status gaps_found → verified). The 6 HUMAN-UAT.md tone items remain pending (out of scope for this plan)."
+ - "All 24 Phase-2 REQ-IDs remain structurally PASS — none of these gap-fix changes regress any existing test (full `npm run ci` exits 0 + Playwright e2e exits 0)."
+
+ artifacts:
+ - path: src/index.css
+ provides: "Global page styles — body bg #1a1a1a, color #e8e0d0, zero margin, full viewport height, serif family, #game-container centered. Imported once from src/main.tsx so Vite bundles it into the entry chunk. ~15 lines."
+ - path: src/index.css.test.ts
+ provides: "Vitest smoke test asserting the CSS rules are present in the source file (file-read assertion; sufficient for a single-file static stylesheet)."
+ - path: src/ui/first-run/FirstRunHint.tsx
+ provides: "FirstRunHint component — renders a single bible-voice line when session.firstRunHintDismissed is false AND session.beginGateDismissed is true; null otherwise. Reads the line from uiStrings[1].first_run_hint (externalized per STRY-09)."
+ exports: ["FirstRunHint"]
+ - path: src/ui/first-run/FirstRunHint.test.tsx
+ provides: "Vitest cases: hidden when beginGateDismissed=false; visible after Begin dismiss; hidden when firstRunHintDismissed=true; renders the externalized string (not hardcoded); auto-dismisses when a plantSeed command commits (selectFirstPlantHasOccurred subscription)."
+ - path: content/seasons/01-soil/ui-strings.yaml
+ provides: "first_run_hint: added under the season-1 UI strings tree. Candidate copy ranked per Step 1: 'Begin where the soil is bare.' (Recommended — bible-voice) / 'The soil is waiting.' (alternative — quieter) / 'Click a tile to plant.' (functional fallback)."
+ - path: src/store/session-slice.ts
+ provides: "Adds `firstRunHintDismissed: boolean` + `dismissFirstRunHint()` action. Session-state only — NEVER added to V1Payload (per scope_constraint #3 — no migrations[2])."
+ - path: src/render/garden/tile-renderer.ts
+ provides: "OUTLINE_COLOR brightened to ~0x5a5a60; OUTLINE_HOVER brightened to ~0x7a7a82; hover adds a slight fill alpha bump on the hit rectangle (no animation noise — pointer-driven, reduced-motion-friendly)."
+ - path: src/render/garden/tile-renderer.test.ts
+ provides: "Vitest cases: drawTiles produces 16 tile groups; outline draw call uses OUTLINE_COLOR=0x5a5a60 (assert via mocked Phaser.Scene.add.graphics call args); pointerover handler uses OUTLINE_HOVER=0x7a7a82 (assert via stubbed event)."
+ - path: src/render/garden/gate-renderer.ts
+ provides: "drawGate() additionally adds a faint vertical wall band Phaser primitive at the gate's column (x ≈ GATE_X) spanning the canvas height (y=0 → y=768), color GATE_COLOR with alpha ~0.18. Stored on GateGameObjects as `wall: Phaser.GameObjects.Rectangle`."
+ - path: src/render/garden/gate-renderer.test.ts
+ provides: "Vitest case: drawGate adds the wall primitive at the expected x with low alpha (assert via mocked Phaser.Scene.add.rectangle call args matching the wall band signature: x near GATE_X, full canvas height, GATE_COLOR, alpha ~0.18)."
+ - path: tests/e2e/season1-loop.spec.ts
+ provides: "Three new assertions threaded into the existing PIPE-07 spec: (a) body computed bg is rgb(26, 26, 26) on first nav, (b) FirstRunHint visible after Begin click, (c) FirstRunHint gone after the first plantSeed enqueue."
+
+ key_links:
+ - from: src/main.tsx
+ to: src/index.css
+ via: "import './index.css'; — Vite bundles the CSS into the entry chunk so body styles apply before React mounts"
+ pattern: "import.*index\\.css"
+ - from: src/App.tsx
+ to: src/ui/first-run/FirstRunHint.tsx
+ via: " mounted alongside Letter/Settings/etc."
+ pattern: "
+**Gap-closure plan. Depends on Plans 02-01..02-05 (all already executed).**
+
+This plan closes the 4 first-impression UX gaps surfaced by the 2026-05-09 live UAT walkthrough on the dev server. All 24 Phase-2 REQ-IDs are structurally PASS; the test suite cannot detect "what does a new player see on frame one?" — the gaps are minimum-viable functional UX, NOT Phase-3 aesthetic deferrals.
+
+**Scope discipline (from 02-VERIFICATION.md frontmatter `gaps:` block):**
+- G1 BLOCKING — no global page CSS — fix shape: src/index.css ~15 lines.
+- G2 BLOCKING — no first-run prompt after Begin — fix shape: tiny FirstRunHint component + session flag.
+- G3 HIGH — tile outlines too dim — fix shape: brighten OUTLINE_COLOR + clearer hover in tile-renderer.ts.
+- G4 MEDIUM — gate visual stands alone — fix shape: faint vertical wall band primitive in gate-renderer.ts.
+
+**What this plan must NOT do:**
+- No painted assets (Phase 3 watercolor deferral preserved).
+- No new npm dependencies (CSS is plain CSS imported via Vite native).
+- No V1Payload changes / no migrations[2] (firstRunHintDismissed is session state, not save state).
+- No re-litigation of typographic Begin screen tone (HUMAN-UAT.md item 4 covers that — out of scope here).
+- No tone judgment on Lura's voice or letter cadence (HUMAN-UAT.md items 1-2 — out of scope).
+
+**Wave structure: single wave (Wave 0) since all 4 gap-fix tasks are file-disjoint and parallel-safe within the plan.** G1, G2, G3, G4 do not overlap on `files_modified`:
+- G1 owns: src/index.css, src/main.tsx, src/index.css.test.ts.
+- G2 owns: content/seasons/01-soil/ui-strings.yaml, src/store/session-slice.ts, src/ui/first-run/* (new files), src/ui/index.ts, src/App.tsx.
+- G3 owns: src/render/garden/tile-renderer.ts, src/render/garden/tile-renderer.test.ts.
+- G4 owns: src/render/garden/gate-renderer.ts, src/render/garden/gate-renderer.test.ts.
+- The Playwright e2e extension touches a shared file (tests/e2e/season1-loop.spec.ts) and runs LAST as Task 5 — the integration/verification task.
+
+**Tasks: 5** (one per gap + a final integration task that extends the e2e and runs full ci). Estimated context cost ~30-40% (well within budget for a gap-closure plan).
+
+
+
+Close the 4 first-impression UX gaps that the 2026-05-09 live UAT surfaced. Each fix uses Phaser primitives or a single CSS file — no painted assets, no new dependencies, no V1Payload changes. After execution: the dark canvas no longer floats in a white viewport (G1), a first-time player sees one bible-voice instructional line after Begin dismisses (G2), the 4×4 tile grid reads as legible interactive surfaces with a clearer hover state (G3), and the gate reads as part of a wall via a faint vertical primitive band (G4).
+
+Purpose: Unblocks `/gsd-verify-work` Phase 2 sign-off. The 4 gaps in 02-VERIFICATION.md frontmatter `gaps:` block clear; the 6 HUMAN-UAT.md tone items remain pending (separate workflow). The Phase-2 vertical slice that "could plausibly ship as a free standalone Season-1 prologue" actually feels like one to a brand-new player — the loop is intuitive on frame one.
+
+Output: 4 gap fixes + 4 unit tests + 3 e2e assertions threaded into tests/e2e/season1-loop.spec.ts. `npm run ci && npm run test:e2e` exits 0. 02-VERIFICATION.md ready for re-verifier pass to flip status from gaps_found → verified.
+
+
+
+@$HOME/.claude/get-shit-done/workflows/execute-plan.md
+@$HOME/.claude/get-shit-done/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@CLAUDE.md
+@.planning/anti-fomo-doctrine.md
+@.planning/phases/02-season-1-vertical-slice-soil/02-CONTEXT.md
+@.planning/phases/02-season-1-vertical-slice-soil/02-PATTERNS.md
+@.planning/phases/02-season-1-vertical-slice-soil/02-VERIFICATION.md
+@.planning/phases/02-season-1-vertical-slice-soil/02-05-letter-settings-e2e-SUMMARY.md
+
+
+
+
+
+From src/main.tsx (currently 14 lines, no CSS import):
+```typescript
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import App from './App.tsx';
+
+const rootEl = document.getElementById('root');
+if (!rootEl) {
+ throw new Error('Root element #root not found in index.html');
+}
+
+createRoot(rootEl).render(
+
+
+
+);
+```
+
+From index.html (the body has only `` — Phaser parents to `#game-container` rendered by `` which renders `` inside `