diff --git a/docs/superpowers/plans/2026-05-09-mvp-login.md b/docs/superpowers/plans/2026-05-09-mvp-login.md
new file mode 100644
index 0000000..4be4907
--- /dev/null
+++ b/docs/superpowers/plans/2026-05-09-mvp-login.md
@@ -0,0 +1,326 @@
+# MVP Login + Family Seed Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task.
+
+**Goal:** Make the UI actually usable — login page, auth gating, family profile seed, and vote-link base URL fix.
+
+**Architecture:** Three changes: (1) React Login page + 401 interceptor in the frontend so unauthenticated users are redirected; (2) Python seed script to insert the first `family_profile` + `family_member` rows (no API endpoint exists to create them); (3) `APP_BASE_URL` added to `.env.test` so vote links in emails point to the right host.
+
+**Tech Stack:** React 18 + TypeScript + Vite + Tailwind, axios, FastAPI, SQLAlchemy 2.0, Postgres 15.
+
+---
+
+## File map
+
+| File | Action |
+|---|---|
+| `frontend/src/pages/Login.tsx` | **Create** — password form, calls `auth.login()`, redirects to `/` |
+| `frontend/src/App.tsx` | **Modify** — add `/login` route, logout button in nav |
+| `frontend/src/api/index.ts` | **Modify** — add 401 response interceptor → redirect to `/login` |
+| `scripts/seed_family.py` | **Create** — inserts `family_profile` + `family_member` rows via SQLAlchemy |
+
+---
+
+## Task 1: Login page + 401 interceptor + nav logout
+
+**Worktree:** `/home/peter/Projects/MealPlanner/.worktrees/feature-mvp-login`
+
+**Files:**
+- Create: `frontend/src/pages/Login.tsx`
+- Modify: `frontend/src/App.tsx`
+- Modify: `frontend/src/api/index.ts`
+
+### Step 1: Create `frontend/src/pages/Login.tsx`
+
+```tsx
+import { useState } from 'react'
+import { mealPlannerApi } from '../api'
+
+export default function Login() {
+ const [password, setPassword] = useState('')
+ const [error, setError] = useState('')
+ const [loading, setLoading] = useState(false)
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault()
+ setError('')
+ setLoading(true)
+ try {
+ await mealPlannerApi.auth.login(password)
+ window.location.href = '/'
+ } catch {
+ setError('Incorrect password. Try again.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ return (
+
+
+
MealPlanner
+
Family login
+
+
+
+ )
+}
+```
+
+### Step 2: Modify `frontend/src/App.tsx`
+
+Add `import Login from './pages/Login'` to the imports block.
+
+Add a logout button to the nav (right side of the flex):
+
+```tsx
+
+
+
+```
+
+Add the Login route inside ``:
+
+```tsx
+} />
+```
+
+The full updated `App.tsx`:
+
+```tsx
+import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
+import Dashboard from './pages/Dashboard'
+import MealDetail from './pages/MealDetail'
+import Pantry from './pages/Pantry'
+import ShoppingList from './pages/ShoppingList'
+import Login from './pages/Login'
+import { mealPlannerApi } from './api'
+
+const queryClient = new QueryClient()
+
+function App() {
+ return (
+
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
+ )
+}
+
+export default App
+```
+
+### Step 3: Add 401 interceptor to `frontend/src/api/index.ts`
+
+After the `api` axios instance is created (after the `const api = axios.create(...)` block), add:
+
+```typescript
+api.interceptors.response.use(
+ response => response,
+ error => {
+ if (error.response?.status === 401 && window.location.pathname !== '/login') {
+ window.location.href = '/login'
+ }
+ return Promise.reject(error)
+ }
+)
+```
+
+### Step 4: Build and verify
+
+```bash
+cd /home/peter/Projects/MealPlanner/.worktrees/feature-mvp-login
+npm --prefix frontend ci
+npm --prefix frontend run build
+# Expected: no TypeScript errors, build succeeds
+```
+
+### Step 5: Commit
+
+```bash
+cd /home/peter/Projects/MealPlanner/.worktrees/feature-mvp-login
+git add frontend/src/pages/Login.tsx \
+ frontend/src/App.tsx \
+ frontend/src/api/index.ts
+git commit -m "feat: login page, 401 interceptor, nav sign-out"
+```
+
+---
+
+## Task 2: Family profile seed script
+
+**File:** Create `scripts/seed_family.py`
+
+This script inserts a `family_profile` row and `family_member` rows directly via SQLAlchemy. Run once after the containers are up.
+
+```python
+#!/usr/bin/env python3
+"""
+One-time seed: creates the Woolery family profile + members.
+
+Run inside the backend container:
+ docker compose --env-file .env.test exec backend python /app/seed_family.py
+
+Or copy and run:
+ docker cp scripts/seed_family.py mealplanner-backend-1:/app/seed_family.py
+ docker compose --env-file .env.test exec backend python /app/seed_family.py
+"""
+import os
+import sys
+
+# ── Edit these before running ────────────────────────────────────────────────
+FAMILY_CALORIE_TARGET = 500 # per serving, per meal
+MEMBERS = [
+ {"name": "Peter", "email": "peter@research.bike", "role": "admin", "likes_mushrooms": True},
+ {"name": "Wife", "email": "", "role": "member", "likes_mushrooms": False},
+ {"name": "Kid 1", "email": "", "role": "member", "likes_mushrooms": False},
+ {"name": "Kid 2", "email": "", "role": "member", "likes_mushrooms": False},
+]
+# ─────────────────────────────────────────────────────────────────────────────
+
+sys.path.insert(0, "/app")
+os.environ.setdefault("DATABASE_URL", os.environ["DATABASE_URL"])
+
+from app.database import SessionLocal
+from app.models import FamilyProfile, FamilyMember, FamilyMemberRole
+
+db = SessionLocal()
+try:
+ existing = db.query(FamilyProfile).first()
+ if existing:
+ print(f"Family profile already exists (id={existing.id}). Nothing to do.")
+ sys.exit(0)
+
+ profile = FamilyProfile(calorie_target=FAMILY_CALORIE_TARGET)
+ db.add(profile)
+ db.flush()
+
+ for m in MEMBERS:
+ if not m["email"]:
+ print(f" Skipping {m['name']} — no email set")
+ continue
+ member = FamilyMember(
+ family_profile_id=profile.id,
+ name=m["name"],
+ email=m["email"],
+ role=FamilyMemberRole[m["role"].upper()],
+ likes_mushrooms=m["likes_mushrooms"],
+ )
+ db.add(member)
+ print(f" Added member: {m['name']} <{m['email']}>")
+
+ db.commit()
+ print(f"Done. Family profile id={profile.id}")
+finally:
+ db.close()
+```
+
+### Step: Commit
+
+```bash
+cd /home/peter/Projects/MealPlanner/.worktrees/feature-mvp-login
+git add scripts/seed_family.py
+git commit -m "feat: one-time family profile seed script"
+```
+
+---
+
+## Post-merge steps (run by operator after merge)
+
+### 1. Add APP_BASE_URL to `.env.test`
+
+Edit `.env.test` on the host and add:
+```
+APP_BASE_URL=http://100.108.224.12:8081
+```
+
+This makes vote links in Friday emails point to the right host.
+
+### 2. Rebuild frontend container and restart
+
+```bash
+docker compose --env-file .env.test build frontend
+docker compose --env-file .env.test up -d
+```
+
+### 3. Run the seed script
+
+```bash
+# Edit scripts/seed_family.py to fill in real names/emails first, then:
+docker cp scripts/seed_family.py mealplanner-backend-1:/app/seed_family.py
+docker compose --env-file .env.test exec backend python /app/seed_family.py
+```
+
+### 4. Verify login works
+
+Open `http://100.108.224.12:8081` — should redirect to `/login`. Enter `SESSION_PASSWORD`. Should land on Dashboard showing "No meal plan generated yet."