diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md
index 4cc3ef4..5a18398 100644
--- a/.planning/ROADMAP.md
+++ b/.planning/ROADMAP.md
@@ -50,7 +50,12 @@ Make FamilySync configurable, administrable, and maintainable for real multi-mem
- **No stale storage-state** (Pitfall 14): use `DEV_AUTH_BYPASS=true` for the automated harness rather than a checked-in storage-state.json with an expiring session cookie; decide the auth strategy before the first test.
- **Service worker block** (Pitfall 15): set `serviceWorkers: 'block'` (or explicitly unregister) in the context so a previous run's SW does not intercept requests / return stale cached responses; verify no SW-sourced responses in the trace.
- Hard constraints: targets the dev build via `DEV_AUTH_BYPASS` (DEV_AUTH_BYPASS user 1 has no CalDAV credential/calendars — verify layout/flows, not live event-create); real prod-service-worker / iOS-Safari-standalone mobile testing stays a human/device gate (out of scope).
-**Plans**: TBD
+**Plans**: 4 plans (3 waves)
+Plans:
+- [ ] 07-01-PLAN.md — Harness foundation: @playwright/test + WebKit/Chromium browsers, playwright.config.ts (iPhone/WebKit + Pixel/Chromium matrix, serviceWorkers block, env baseURL, vite webServer), vitest exclude, scripts (Wave 1)
+- [ ] 07-02-PLAN.md — global-setup.ts: /health readiness poll + deterministic mysql2 reset-and-seed (calendar id 10 INSERT IGNORE guard, list + items) + e2e README/guardrails (Wave 2)
+- [ ] 07-03-PLAN.md — layout.spec.ts: tap targets >=44px, no overflow, in-viewport, accessible names (UI-SPEC Rules 1-4) + harness self-validation injected-defect proofs (Wave 3)
+- [ ] 07-04-PLAN.md — calendar.spec.ts + lists.spec.ts: populated/empty/error states (Rules 4/5) + DEV_AUTH_BYPASS auth-reached + no-SW-controller precondition (Wave 3)
**UI hint**: yes
### Phase 8: Gitea CI
diff --git a/.planning/phases/07-mobile-test-harness/07-01-PLAN.md b/.planning/phases/07-mobile-test-harness/07-01-PLAN.md
new file mode 100644
index 0000000..b1380e4
--- /dev/null
+++ b/.planning/phases/07-mobile-test-harness/07-01-PLAN.md
@@ -0,0 +1,179 @@
+---
+phase: 07-mobile-test-harness
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - apps/pwa/package.json
+ - apps/pwa/playwright.config.ts
+ - apps/pwa/vitest.config.ts
+ - apps/pwa/tsconfig.json
+ - package.json
+autonomous: true
+requirements: [TEST-01, TEST-02]
+user_setup: []
+
+must_haves:
+ truths:
+ - "playwright test --list reports exactly two projects: iphone and pixel"
+ - "Vitest does NOT pick up e2e/*.spec.ts files (no glob collision)"
+ - "Both WebKit and Chromium browser engines are installed for @playwright/test"
+ - "tsc --noEmit passes in apps/pwa with the new playwright.config.ts and e2e/ files in scope"
+ artifacts:
+ - path: "apps/pwa/playwright.config.ts"
+ provides: "Two-project device matrix (iPhone/WebKit, Pixel/Chromium), serviceWorkers block, env baseURL, globalSetup ref, vite-only webServer, trace/artifact config"
+ contains: "devices['iPhone 14']"
+ - path: "apps/pwa/vitest.config.ts"
+ provides: "exclude e2e/** so Vitest's default *.spec.ts glob does not collide with Playwright specs"
+ contains: "exclude"
+ - path: "apps/pwa/package.json"
+ provides: "@playwright/test devDependency + test:e2e scripts"
+ contains: "test:e2e"
+ key_links:
+ - from: "apps/pwa/playwright.config.ts"
+ to: "apps/pwa/e2e/global-setup.ts"
+ via: "globalSetup config option"
+ pattern: "globalSetup.*global-setup"
+ - from: "apps/pwa/playwright.config.ts"
+ to: "PLAYWRIGHT_BASE_URL env"
+ via: "use.baseURL env-driven"
+ pattern: "PLAYWRIGHT_BASE_URL"
+---
+
+
+Stand up the Playwright test-harness foundation in `apps/pwa`: add `@playwright/test` as a dev dependency, install the WebKit + Chromium browser engines, author `playwright.config.ts` with the two-profile device matrix (iPhone/WebKit + Pixel/Chromium), and isolate the new `e2e/*.spec.ts` glob from the existing Vitest `*.spec.ts` default glob. This is the blocking dependency for the seed plan and all spec plans.
+
+Purpose: Every downstream plan (global-setup, layout/calendar/lists specs) imports from `@playwright/test` and runs under this config. Nothing else in the phase can land until the config matrix, browser engines, and glob isolation exist.
+
+Output: `apps/pwa/playwright.config.ts`, the `@playwright/test` dev dep + installed browsers, `vitest.config.ts` exclude, package.json scripts, and `e2e/` brought into the `tsc --noEmit` gate.
+
+
+
+@$HOME/.claude/gsd-core/workflows/execute-plan.md
+@$HOME/.claude/gsd-core/templates/summary.md
+
+
+
+@.planning/PROJECT.md
+@.planning/ROADMAP.md
+@.planning/STATE.md
+@.planning/phases/07-mobile-test-harness/07-CONTEXT.md
+@.planning/phases/07-mobile-test-harness/07-RESEARCH.md
+@.planning/phases/07-mobile-test-harness/07-PATTERNS.md
+@.planning/phases/07-mobile-test-harness/07-VALIDATION.md
+
+
+
+
+
+ Task 1: Install @playwright/test + browser engines, wire package.json scripts
+ apps/pwa/package.json, package.json
+
+ - apps/pwa/package.json — current scripts block (`dev`, `build`, `preview`, `typecheck`, `test`) and devDependencies; mirror naming
+ - package.json (root) — workspace script convention: `pnpm --filter @familysync/