diff --git a/docs/superpowers/plans/2026-03-11-therapyfinder-v1.md b/docs/superpowers/plans/2026-03-11-therapyfinder-v1.md
new file mode 100644
index 0000000..c014510
--- /dev/null
+++ b/docs/superpowers/plans/2026-03-11-therapyfinder-v1.md
@@ -0,0 +1,1249 @@
+# TherapyFinder V1 Implementation Plan
+
+> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Build a local-first PWA that guides GKV-insured users through the psychotherapy access process — from first Sprechstunde to Kostenerstattungsantrag — with structured contact/rejection tracking and PDF export.
+
+**Architecture:** Local-first PWA with all data stored in PGlite (IndexedDB). No backend for V1 — sensitive health data never leaves the device. The app is a multi-step process guide with a contact tracker that produces exportable rejection documentation. Therapist directory integration is deferred to V2 (data source still open).
+
+**Tech Stack:** Bun, Vite, React 19, TanStack Router (file-based), shadcn/ui + Tailwind CSS v4, PGlite, Zustand, TanStack Form + Zod, vite-plugin-pwa, Vitest, Playwright, Biome
+
+---
+
+## Scope
+
+**In scope (V1):**
+- Project scaffolding and tooling
+- PGlite database with schema and migrations
+- Onboarding flow (capture insurance info, current process step)
+- Process stepper dashboard (5 phases, interactive checklist)
+- Contact tracker (log therapist contacts, outcomes, notes)
+- Rejection list PDF export (for Kostenerstattungsantrag)
+- Kostenerstattungs-Assistent (checklist + guidance)
+- PWA (offline-capable, installable)
+
+**Deferred (V2+):**
+- Therapist directory / KV-Verzeichnis integration
+- Push notifications / reminders
+- Multi-language support (i18n)
+- Backend / cloud sync
+
+---
+
+## File Structure
+
+```
+tpf/
+├── .mise.toml
+├── biome.json
+├── package.json
+├── tsconfig.json
+├── vite.config.ts
+├── index.html
+├── public/
+│ ├── manifest.webmanifest
+│ └── icons/
+│ ├── icon-192.png
+│ └── icon-512.png
+├── src/
+│ ├── main.tsx
+│ ├── app.tsx
+│ ├── routes/
+│ │ ├── __root.tsx
+│ │ ├── index.tsx # redirects to /prozess or /onboarding
+│ │ ├── onboarding/
+│ │ │ └── index.tsx # Onboarding form
+│ │ ├── prozess/
+│ │ │ └── index.tsx # Process stepper dashboard
+│ │ ├── kontakte/
+│ │ │ ├── index.tsx # Contact list
+│ │ │ └── neu.tsx # Add/edit contact
+│ │ └── antrag/
+│ │ └── index.tsx # Kostenerstattungs-Assistent
+│ ├── features/
+│ │ ├── onboarding/
+│ │ │ ├── components/
+│ │ │ │ └── onboarding-form.tsx
+│ │ │ ├── schema.ts
+│ │ │ └── index.ts
+│ │ ├── prozess/
+│ │ │ ├── components/
+│ │ │ │ ├── process-stepper.tsx
+│ │ │ │ └── phase-card.tsx
+│ │ │ ├── schema.ts
+│ │ │ ├── hooks.ts
+│ │ │ └── index.ts
+│ │ ├── kontakte/
+│ │ │ ├── components/
+│ │ │ │ ├── contact-form.tsx
+│ │ │ │ ├── contact-list.tsx
+│ │ │ │ └── contact-card.tsx
+│ │ │ ├── schema.ts
+│ │ │ ├── hooks.ts
+│ │ │ └── index.ts
+│ │ └── antrag/
+│ │ ├── components/
+│ │ │ ├── antrag-checklist.tsx
+│ │ │ └── pdf-export-button.tsx
+│ │ ├── schema.ts
+│ │ ├── pdf.ts # PDF generation logic
+│ │ └── index.ts
+│ └── shared/
+│ ├── components/
+│ │ └── ui/ # shadcn/ui components (generated)
+│ ├── hooks/
+│ │ └── use-db.ts # PGlite query hook
+│ ├── db/
+│ │ ├── client.ts # PGlite singleton
+│ │ ├── migrations/
+│ │ │ └── 001_init.sql
+│ │ └── schema.ts # Zod schemas mirroring DB tables
+│ └── lib/
+│ ├── utils.ts # cn() and helpers
+│ └── constants.ts # Process phases, enum labels
+├── e2e/
+│ ├── onboarding.spec.ts
+│ └── kontakte.spec.ts
+└── scripts/
+ └── deploy.sh
+```
+
+---
+
+## Chunk 1: Project Scaffolding
+
+### Task 1: Initialize project with Vite + React + TypeScript
+
+**Files:**
+- Create: `.mise.toml`
+- Create: `package.json` (via `bun create`)
+- Create: `tsconfig.json`
+- Create: `vite.config.ts`
+- Create: `index.html`
+- Create: `src/main.tsx`
+- Create: `biome.json`
+
+- [ ] **Step 1: Create `.mise.toml`**
+
+```toml
+[tools]
+bun = "1.2.5"
+```
+
+- [ ] **Step 2: Install Bun via mise**
+
+Run: `mise install`
+
+- [ ] **Step 3: Scaffold Vite project**
+
+Run: `bun create vite . --template react-ts`
+
+Accept overwrite prompts for existing files. This generates `package.json`, `tsconfig.json`, `vite.config.ts`, `index.html`, `src/main.tsx`, `src/App.tsx`, etc.
+
+- [ ] **Step 4: Install dependencies**
+
+```bash
+bun add react@latest react-dom@latest
+bun add -d @types/react @types/react-dom typescript vite @vitejs/plugin-react
+```
+
+- [ ] **Step 5: Configure Biome**
+
+Create `biome.json`:
+```json
+{
+ "$schema": "https://biomejs.dev/schemas/2.0.0-beta.1/schema.json",
+ "organizeImports": {
+ "enabled": true
+ },
+ "formatter": {
+ "indentStyle": "tab"
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true
+ }
+ }
+}
+```
+
+Run: `bun add -d @biomejs/biome`
+
+- [ ] **Step 6: Add scripts to `package.json`**
+
+Ensure these scripts exist:
+```json
+{
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview",
+ "test": "vitest run",
+ "e2e": "playwright test",
+ "lint": "biome check .",
+ "format": "biome check --write ."
+ }
+}
+```
+
+- [ ] **Step 7: Verify dev server starts**
+
+Run: `bun dev` — should open on `http://localhost:5173` with the default Vite template.
+
+- [ ] **Step 8: Initialize git repo and commit**
+
+```bash
+git init
+```
+
+Add `.gitignore` with: `node_modules/`, `dist/`, `.env`, `.env.local`, `.mise.local.toml`
+
+```bash
+git add -A && git commit -m "scaffold vite + react + typescript project"
+```
+
+---
+
+### Task 2: Add TanStack Router (file-based routing)
+
+**Files:**
+- Modify: `vite.config.ts`
+- Modify: `src/main.tsx`
+- Create: `src/app.tsx`
+- Create: `src/routes/__root.tsx`
+- Create: `src/routes/index.tsx`
+
+- [ ] **Step 1: Install TanStack Router**
+
+```bash
+bun add @tanstack/react-router @tanstack/router-plugin
+```
+
+- [ ] **Step 2: Configure Vite plugin**
+
+Update `vite.config.ts`:
+```ts
+import { TanStackRouterVite } from "@tanstack/router-plugin/vite"
+import react from "@vitejs/plugin-react"
+import { defineConfig } from "vite"
+
+export default defineConfig({
+ plugins: [TanStackRouterVite(), react()],
+})
+```
+
+- [ ] **Step 3: Create root layout**
+
+Create `src/routes/__root.tsx`:
+```tsx
+import { Outlet, createRootRoute } from "@tanstack/react-router"
+
+export const Route = createRootRoute({
+ component: () => (
+
+
+
+
+
+ ),
+})
+```
+
+- [ ] **Step 4: Create index route**
+
+Create `src/routes/index.tsx`:
+```tsx
+import { createFileRoute } from "@tanstack/react-router"
+
+export const Route = createFileRoute("/")({
+ component: () => TherapyFinder
,
+})
+```
+
+- [ ] **Step 5: Create app entry**
+
+Create `src/app.tsx`:
+```tsx
+import { RouterProvider, createRouter } from "@tanstack/react-router"
+import { routeTree } from "./routeTree.gen"
+
+const router = createRouter({ routeTree })
+
+declare module "@tanstack/react-router" {
+ interface Register {
+ router: typeof router
+ }
+}
+
+export function App() {
+ return
+}
+```
+
+- [ ] **Step 6: Update `src/main.tsx`**
+
+```tsx
+import { StrictMode } from "react"
+import { createRoot } from "react-dom/client"
+import { App } from "./app"
+import "./index.css"
+
+createRoot(document.getElementById("root")!).render(
+
+
+ ,
+)
+```
+
+- [ ] **Step 7: Delete unused template files**
+
+Remove: `src/App.tsx`, `src/App.css`, `src/assets/` (Vite template leftovers).
+
+- [ ] **Step 8: Verify routing works**
+
+Run: `bun dev` — navigate to `/` and confirm "TherapyFinder" heading appears.
+
+- [ ] **Step 9: Commit**
+
+```bash
+git add -A && git commit -m "add tanstack router with file-based routing"
+```
+
+---
+
+### Task 3: Add Tailwind CSS v4 + shadcn/ui
+
+**Files:**
+- Modify: `package.json`
+- Modify: `src/index.css`
+- Create: `src/shared/lib/utils.ts`
+- Create: `src/shared/components/ui/` (generated by shadcn)
+
+- [ ] **Step 1: Install Tailwind CSS v4**
+
+```bash
+bun add tailwindcss @tailwindcss/vite
+```
+
+Update `vite.config.ts` to add the Tailwind plugin:
+```ts
+import tailwindcss from "@tailwindcss/vite"
+import { TanStackRouterVite } from "@tanstack/router-plugin/vite"
+import react from "@vitejs/plugin-react"
+import { defineConfig } from "vite"
+
+export default defineConfig({
+ plugins: [TanStackRouterVite(), react(), tailwindcss()],
+})
+```
+
+- [ ] **Step 2: Configure CSS entry**
+
+Replace `src/index.css` contents with:
+```css
+@import "tailwindcss";
+```
+
+- [ ] **Step 3: Install and configure shadcn/ui**
+
+Run: `bunx shadcn@latest init`
+
+Select: New York style, Zinc color, CSS variables: yes.
+
+This creates `components.json` and sets up the `src/shared/components/ui/` directory.
+
+- [ ] **Step 4: Create `cn()` utility**
+
+Create `src/shared/lib/utils.ts`:
+```ts
+import { type ClassValue, clsx } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
+```
+
+Run: `bun add clsx tailwind-merge`
+
+- [ ] **Step 5: Add initial shadcn components**
+
+```bash
+bunx shadcn@latest add button card badge progress separator
+```
+
+- [ ] **Step 6: Verify styling works**
+
+Update `src/routes/index.tsx` to use a Button, run `bun dev`, confirm styled button renders.
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add -A && git commit -m "add tailwind css v4, shadcn/ui with initial components"
+```
+
+---
+
+### Task 4: Add PGlite (local database)
+
+**Files:**
+- Create: `src/shared/db/client.ts`
+- Create: `src/shared/db/migrations/001_init.sql`
+- Create: `src/shared/db/schema.ts`
+- Create: `src/shared/hooks/use-db.ts`
+
+- [ ] **Step 1: Install PGlite**
+
+Run: `bun add @electric-sql/pglite`
+
+- [ ] **Step 2: Create PGlite singleton**
+
+Create `src/shared/db/client.ts`:
+```ts
+import { PGlite } from "@electric-sql/pglite"
+
+let _db: PGlite | null = null
+
+export async function getDb(): Promise {
+ if (!_db) {
+ _db = new PGlite("idb://therapyfinder")
+ await runMigrations(_db)
+ }
+ return _db
+}
+
+async function runMigrations(db: PGlite): Promise {
+ await db.exec(`
+ CREATE TABLE IF NOT EXISTS _migrations (
+ id SERIAL PRIMARY KEY,
+ name TEXT NOT NULL UNIQUE,
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+ )
+ `)
+
+ const migrations = import.meta.glob("./migrations/*.sql", {
+ query: "?raw",
+ import: "default",
+ })
+
+ const sortedPaths = Object.keys(migrations).sort()
+
+ for (const path of sortedPaths) {
+ const name = path.split("/").pop()!
+ const result = await db.query(
+ "SELECT 1 FROM _migrations WHERE name = $1",
+ [name],
+ )
+ if (result.rows.length > 0) continue
+
+ const sql = (await migrations[path]()) as string
+ await db.exec(sql)
+ await db.query(
+ "INSERT INTO _migrations (name) VALUES ($1)",
+ [name],
+ )
+ }
+}
+```
+
+- [ ] **Step 3: Create initial migration**
+
+Create `src/shared/db/migrations/001_init.sql`:
+```sql
+-- User profile (single row per device)
+CREATE TABLE nutzer (
+ id SERIAL PRIMARY KEY,
+ name TEXT,
+ plz TEXT,
+ ort TEXT,
+ krankenkasse TEXT,
+ aktueller_schritt TEXT NOT NULL DEFAULT 'neu',
+ dringlichkeitscode BOOLEAN NOT NULL DEFAULT FALSE,
+ dringlichkeitscode_datum DATE,
+ tss_beantragt BOOLEAN NOT NULL DEFAULT FALSE,
+ tss_beantragt_datum DATE,
+ erstellt_am TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ aktualisiert_am TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+-- Therapist contacts
+CREATE TABLE therapeut (
+ id SERIAL PRIMARY KEY,
+ name TEXT NOT NULL,
+ adresse TEXT,
+ plz TEXT,
+ stadt TEXT,
+ telefon TEXT,
+ email TEXT,
+ website TEXT,
+ therapieform TEXT,
+ kassenzulassung TEXT DEFAULT 'gkv',
+ erstellt_am TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+-- Contact attempts
+CREATE TABLE kontakt (
+ id SERIAL PRIMARY KEY,
+ therapeut_id INTEGER NOT NULL REFERENCES therapeut(id) ON DELETE CASCADE,
+ datum DATE NOT NULL DEFAULT CURRENT_DATE,
+ kanal TEXT NOT NULL DEFAULT 'telefon',
+ ergebnis TEXT NOT NULL DEFAULT 'keine_antwort',
+ notiz TEXT,
+ antwort_datum DATE,
+ erstellt_am TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+-- Sprechstunden visits
+CREATE TABLE sprechstunde (
+ id SERIAL PRIMARY KEY,
+ therapeut_id INTEGER NOT NULL REFERENCES therapeut(id) ON DELETE CASCADE,
+ datum DATE NOT NULL,
+ ergebnis TEXT,
+ diagnose TEXT,
+ dringlichkeitscode BOOLEAN NOT NULL DEFAULT FALSE,
+ erstellt_am TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+```
+
+- [ ] **Step 4: Create Zod schemas**
+
+Create `src/shared/db/schema.ts`:
+```ts
+import { z } from "zod"
+
+export const prozessSchrittEnum = z.enum([
+ "neu",
+ "sprechstunde_absolviert",
+ "diagnose_erhalten",
+ "tss_beantragt",
+ "eigensuche",
+ "antrag_gestellt",
+])
+export type ProzessSchritt = z.infer
+
+export const kontaktKanalEnum = z.enum([
+ "telefon",
+ "email",
+ "online_formular",
+ "persoenlich",
+])
+export type KontaktKanal = z.infer
+
+export const kontaktErgebnisEnum = z.enum([
+ "keine_antwort",
+ "absage",
+ "warteliste",
+ "zusage",
+])
+export type KontaktErgebnis = z.infer
+
+export const therapieformEnum = z.enum([
+ "verhaltenstherapie",
+ "tiefenpsychologisch",
+ "analytisch",
+ "systemisch",
+])
+export type Therapieform = z.infer
+
+export const nutzerSchema = z.object({
+ id: z.number(),
+ name: z.string().nullable(),
+ plz: z.string().nullable(),
+ ort: z.string().nullable(),
+ krankenkasse: z.string().nullable(),
+ aktueller_schritt: prozessSchrittEnum,
+ dringlichkeitscode: z.boolean(),
+ dringlichkeitscode_datum: z.string().nullable(),
+ tss_beantragt: z.boolean(),
+ tss_beantragt_datum: z.string().nullable(),
+})
+
+export const therapeutSchema = z.object({
+ id: z.number(),
+ name: z.string(),
+ adresse: z.string().nullable(),
+ plz: z.string().nullable(),
+ stadt: z.string().nullable(),
+ telefon: z.string().nullable(),
+ email: z.string().nullable(),
+ website: z.string().nullable(),
+ therapieform: z.string().nullable(),
+ kassenzulassung: z.string().nullable(),
+})
+
+export const kontaktSchema = z.object({
+ id: z.number(),
+ therapeut_id: z.number(),
+ datum: z.string(),
+ kanal: kontaktKanalEnum,
+ ergebnis: kontaktErgebnisEnum,
+ notiz: z.string().nullable(),
+ antwort_datum: z.string().nullable(),
+})
+
+export const sprechstundeSchema = z.object({
+ id: z.number(),
+ therapeut_id: z.number(),
+ datum: z.string(),
+ ergebnis: z.string().nullable(),
+ diagnose: z.string().nullable(),
+ dringlichkeitscode: z.boolean(),
+})
+```
+
+Run: `bun add zod`
+
+- [ ] **Step 5: Create database hook**
+
+Create `src/shared/hooks/use-db.ts`:
+```ts
+import { useCallback, useEffect, useState } from "react"
+import { getDb } from "../db/client"
+
+export function useDbQuery(
+ query: string,
+ params: unknown[] = [],
+ deps: unknown[] = [],
+) {
+ const [data, setData] = useState([])
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState(null)
+
+ const refetch = useCallback(async () => {
+ setLoading(true)
+ try {
+ const db = await getDb()
+ const result = await db.query(query, params)
+ setData(result.rows as T[])
+ setError(null)
+ } catch (e) {
+ setError(e instanceof Error ? e : new Error(String(e)))
+ } finally {
+ setLoading(false)
+ }
+ }, [query, ...deps])
+
+ useEffect(() => {
+ refetch()
+ }, [refetch])
+
+ return { data, loading, error, refetch }
+}
+
+export async function dbExec(query: string, params: unknown[] = []) {
+ const db = await getDb()
+ return db.query(query, params)
+}
+```
+
+- [ ] **Step 6: Verify database initializes**
+
+Update `src/routes/index.tsx` temporarily to test DB connection. Run `bun dev` — confirm DB initializes (check DevTools → Application → IndexedDB for `therapyfinder`).
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add -A && git commit -m "add pglite with migration runner, zod schemas, db hook"
+```
+
+---
+
+### Task 5: Add Vitest + lint-staged + simple-git-hooks
+
+**Files:**
+- Modify: `package.json`
+- Create: `vitest.config.ts`
+- Create: `src/test-setup.ts`
+- Create: `src/shared/db/schema.test.ts`
+
+- [ ] **Step 1: Install test and code quality tooling**
+
+```bash
+bun add -d vitest @testing-library/react @testing-library/jest-dom jsdom
+bun add -d lint-staged simple-git-hooks
+```
+
+- [ ] **Step 2: Create Vitest config**
+
+Create `vitest.config.ts`:
+```ts
+import { defineConfig } from "vitest/config"
+import react from "@vitejs/plugin-react"
+import { resolve } from "node:path"
+
+export default defineConfig({
+ plugins: [react()],
+ test: {
+ environment: "jsdom",
+ setupFiles: ["./src/test-setup.ts"],
+ include: ["src/**/*.test.{ts,tsx}"],
+ },
+ resolve: {
+ alias: {
+ "@": resolve(__dirname, "src"),
+ },
+ },
+})
+```
+
+Create `src/test-setup.ts`:
+```ts
+import "@testing-library/jest-dom/vitest"
+```
+
+- [ ] **Step 3: Configure lint-staged + simple-git-hooks in `package.json`**
+
+Add to `package.json`:
+```json
+{
+ "simple-git-hooks": {
+ "pre-commit": "bunx lint-staged"
+ },
+ "lint-staged": {
+ "*.{ts,tsx,json,css}": ["biome check --write"]
+ }
+}
+```
+
+Run: `bunx simple-git-hooks`
+
+- [ ] **Step 4: Write a smoke test to verify setup**
+
+Create `src/shared/db/schema.test.ts`:
+```ts
+import { describe, expect, it } from "vitest"
+import { prozessSchrittEnum } from "./schema"
+
+describe("prozessSchrittEnum", () => {
+ it("accepts valid steps", () => {
+ expect(prozessSchrittEnum.parse("neu")).toBe("neu")
+ expect(prozessSchrittEnum.parse("eigensuche")).toBe("eigensuche")
+ })
+
+ it("rejects invalid steps", () => {
+ expect(() => prozessSchrittEnum.parse("invalid")).toThrow()
+ })
+})
+```
+
+- [ ] **Step 5: Run tests**
+
+Run: `bun test`
+Expected: 2 tests pass.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add -A && git commit -m "add vitest, testing library, lint-staged, simple-git-hooks"
+```
+
+---
+
+### Task 6: Add PWA support
+
+**Files:**
+- Modify: `vite.config.ts`
+- Create: `public/manifest.webmanifest`
+- Create: `public/icons/icon-192.png` (placeholder)
+- Create: `public/icons/icon-512.png` (placeholder)
+
+- [ ] **Step 1: Install vite-plugin-pwa**
+
+Run: `bun add -d vite-plugin-pwa`
+
+- [ ] **Step 2: Configure PWA plugin**
+
+Update `vite.config.ts` to add `VitePWA` with `registerType: "autoUpdate"` and `globPatterns: ["**/*.{js,css,html,wasm,data}"]`. Use `manifest: false` (external manifest file).
+
+- [ ] **Step 3: Create manifest**
+
+Create `public/manifest.webmanifest`:
+```json
+{
+ "name": "TherapyFinder",
+ "short_name": "TherapyFinder",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#ffffff",
+ "theme_color": "#09090b",
+ "icons": [
+ {
+ "src": "/icons/icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/icons/icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}
+```
+
+- [ ] **Step 4: Add manifest link to `index.html`**
+
+Add to ``:
+```html
+
+
+```
+
+- [ ] **Step 5: Create placeholder icons**
+
+Generate simple placeholder PNGs (solid color squares) for 192x192 and 512x512. Replace with proper icons later.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add -A && git commit -m "add pwa support with vite-plugin-pwa, manifest, placeholder icons"
+```
+
+---
+
+## Chunk 2: Onboarding and Process Stepper
+
+### Task 7: Define constants and label maps
+
+**Files:**
+- Create: `src/shared/lib/constants.ts`
+- Create: `src/shared/lib/constants.test.ts`
+
+- [ ] **Step 1: Create constants file**
+
+Create `src/shared/lib/constants.ts` with:
+- `PROZESS_SCHRITTE`: array of `{ key, label, beschreibung }` for all 6 process steps (neu → antrag_gestellt)
+- `KANAL_LABELS`: `Record` mapping enum values to German labels
+- `ERGEBNIS_LABELS`: `Record` mapping enum values to German labels
+- `THERAPIEFORM_LABELS`: `Record` mapping enum values to German labels
+
+Labels:
+- neu → "Noch nicht begonnen"
+- sprechstunde_absolviert → "Sprechstunde absolviert"
+- diagnose_erhalten → "Diagnose erhalten"
+- tss_beantragt → "TSS kontaktiert"
+- eigensuche → "Eigensuche läuft"
+- antrag_gestellt → "Kostenerstattung beantragt"
+- telefon → "Telefon", email → "E-Mail", online_formular → "Online-Formular", persoenlich → "Persönlich"
+- keine_antwort → "Keine Antwort", absage → "Absage", warteliste → "Warteliste", zusage → "Zusage"
+- verhaltenstherapie → "Verhaltenstherapie (VT)", tiefenpsychologisch → "Tiefenpsychologisch fundierte PT (TP)", analytisch → "Analytische Psychotherapie (AP)", systemisch → "Systemische Therapie"
+
+- [ ] **Step 2: Write test for constants**
+
+Create `src/shared/lib/constants.test.ts`: verify PROZESS_SCHRITTE has 6 entries, correct order, and all have non-empty label/beschreibung.
+
+- [ ] **Step 3: Run tests, commit**
+
+Run: `bun test`
+
+```bash
+git add -A && git commit -m "add process step constants, channel/result label maps"
+```
+
+---
+
+### Task 8: Onboarding flow
+
+**Files:**
+- Create: `src/features/onboarding/schema.ts`
+- Create: `src/features/onboarding/schema.test.ts`
+- Create: `src/features/onboarding/components/onboarding-form.tsx`
+- Create: `src/features/onboarding/index.ts`
+- Create: `src/routes/onboarding/index.tsx`
+- Modify: `src/routes/index.tsx` (redirect logic)
+
+- [ ] **Step 1: Install TanStack Form**
+
+```bash
+bun add @tanstack/react-form
+```
+
+- [ ] **Step 2: Add shadcn form components**
+
+```bash
+bunx shadcn@latest add input label select
+```
+
+- [ ] **Step 3: Create onboarding schema**
+
+Create `src/features/onboarding/schema.ts` with a Zod schema for: `name` (min 1), `plz` (regex 5 digits), `ort` (min 1), `krankenkasse` (min 1), `aktueller_schritt` (prozessSchrittEnum).
+
+- [ ] **Step 4: Write schema tests**
+
+Create `src/features/onboarding/schema.test.ts`: test valid data accepts, invalid PLZ rejects, empty name rejects.
+
+- [ ] **Step 5: Run tests**
+
+Run: `bun test` — all pass.
+
+- [ ] **Step 6: Create onboarding form component**
+
+Create `src/features/onboarding/components/onboarding-form.tsx`:
+- Uses `useForm` from TanStack Form with the onboarding schema
+- Fields: name, PLZ + Ort (grid), Krankenkasse, aktueller_schritt (select dropdown using PROZESS_SCHRITTE)
+- On submit: INSERT into `nutzer` table via `dbExec`, then navigate to `/prozess`
+- Welcoming header text explaining the app's purpose
+
+- [ ] **Step 7: Create onboarding route**
+
+Create `src/routes/onboarding/index.tsx` rendering ``.
+
+- [ ] **Step 8: Create feature index**
+
+Create `src/features/onboarding/index.ts` re-exporting form and schema.
+
+- [ ] **Step 9: Update root route with redirect logic**
+
+Update `src/routes/index.tsx`: in `beforeLoad`, query nutzer table. If no rows → redirect to `/onboarding`. If rows exist → redirect to `/prozess`.
+
+- [ ] **Step 10: Verify onboarding flow**
+
+Run: `bun dev` → auto-redirect to `/onboarding` → fill form → submit → redirect to `/prozess`.
+
+- [ ] **Step 11: Commit**
+
+```bash
+git add -A && git commit -m "add onboarding flow with form, validation, db persistence"
+```
+
+---
+
+### Task 9: Process stepper dashboard
+
+**Files:**
+- Create: `src/features/prozess/hooks.ts`
+- Create: `src/features/prozess/components/phase-card.tsx`
+- Create: `src/features/prozess/components/process-stepper.tsx`
+- Create: `src/features/prozess/index.ts`
+- Create: `src/routes/prozess/index.tsx`
+
+- [ ] **Step 1: Create process data hooks**
+
+Create `src/features/prozess/hooks.ts`:
+- `useNutzer()`: query nutzer table, return first row
+- `useKontaktStats()`: aggregate query on kontakt table returning gesamt, absagen, warteliste, keine_antwort counts
+- `updateSchritt(schritt)`: UPDATE nutzer aktueller_schritt
+
+- [ ] **Step 2: Create phase card component**
+
+Create `src/features/prozess/components/phase-card.tsx`:
+- Props: label, beschreibung, status ("erledigt" | "aktuell" | "offen"), index
+- Renders a Card with a numbered circle (checkmark if erledigt), label, "Aktuell" badge if current, description only shown for current step
+- Visual styling: ring/border highlight for current, opacity-60 for completed
+
+- [ ] **Step 3: Create process stepper component**
+
+Create `src/features/prozess/components/process-stepper.tsx`:
+- Props: aktuellerSchritt, kontaktGesamt, absagen
+- Renders heading "Dein Fortschritt" with step X of 6
+- Maps PROZESS_SCHRITTE to PhaseCard components with correct status
+- Shows contact stats card with links to `/kontakte` and `/antrag` when step >= "tss_beantragt"
+
+- [ ] **Step 4: Create process route**
+
+Create `src/routes/prozess/index.tsx`: uses `useNutzer()` and `useKontaktStats()` hooks, renders ``.
+
+- [ ] **Step 5: Create feature index**
+
+Create `src/features/prozess/index.ts` re-exporting components and hooks.
+
+- [ ] **Step 6: Verify process stepper**
+
+Run: `bun dev` → complete onboarding → verify stepper shows correct highlighted phase.
+
+- [ ] **Step 7: Commit**
+
+```bash
+git add -A && git commit -m "add process stepper dashboard with phase cards, contact stats"
+```
+
+---
+
+## Chunk 3: Contact Tracker
+
+### Task 10: Contact tracker — data layer and schemas
+
+**Files:**
+- Create: `src/features/kontakte/schema.ts`
+- Create: `src/features/kontakte/schema.test.ts`
+- Create: `src/features/kontakte/hooks.ts`
+- Create: `src/features/kontakte/index.ts`
+
+- [ ] **Step 1: Create contact feature schemas**
+
+Create `src/features/kontakte/schema.ts`:
+- `therapeutFormSchema`: name (required), optional adresse, plz (5-digit regex or empty), stadt, telefon, email (valid or empty), website, therapieform
+- `kontaktFormSchema`: therapeut_id (number), datum (required), kanal (kontaktKanalEnum), ergebnis (kontaktErgebnisEnum), notiz (optional)
+
+- [ ] **Step 2: Write schema tests**
+
+Create `src/features/kontakte/schema.test.ts`: test valid therapist, required name, valid contact, required date.
+
+- [ ] **Step 3: Run tests**
+
+Run: `bun test` — all pass.
+
+- [ ] **Step 4: Create contact hooks**
+
+Create `src/features/kontakte/hooks.ts`:
+- `useTherapeutenListe()`: query therapeut table joined with latest kontakt subqueries (letzter_kontakt, letztes_ergebnis, kontakte_gesamt)
+- `useKontakteForTherapeut(therapeutId)`: query kontakt table filtered by therapeut_id
+- `createTherapeut(data)`: INSERT into therapeut, RETURNING id
+- `createKontakt(data)`: INSERT into kontakt
+
+- [ ] **Step 5: Create feature index**
+
+Create `src/features/kontakte/index.ts` re-exporting schemas and hooks.
+
+- [ ] **Step 6: Commit**
+
+```bash
+git add -A && git commit -m "add contact tracker data layer: schemas, hooks, db queries"
+```
+
+---
+
+### Task 11: Contact tracker — UI components
+
+**Files:**
+- Create: `src/features/kontakte/components/contact-card.tsx`
+- Create: `src/features/kontakte/components/contact-list.tsx`
+- Create: `src/features/kontakte/components/contact-form.tsx`
+- Create: `src/routes/kontakte/index.tsx`
+- Create: `src/routes/kontakte/neu.tsx`
+- Modify: `src/routes/__root.tsx` (add bottom navigation)
+
+- [ ] **Step 1: Add shadcn components**
+
+```bash
+bunx shadcn@latest add textarea dialog
+```
+
+- [ ] **Step 2: Create contact card**
+
+Create `src/features/kontakte/components/contact-card.tsx`:
+- Shows therapist name, city, last contact date, result badge (color-coded: zusage=default, warteliste=secondary, absage=destructive, keine_antwort=outline), contact count
+- Entire card is a Link to `/kontakte/neu?therapeutId=`
+
+- [ ] **Step 3: Create contact list**
+
+Create `src/features/kontakte/components/contact-list.tsx`:
+- Heading "Kontakte" with "+ Neu" button linking to `/kontakte/neu`
+- Empty state with CTA to add first contact
+- Maps useTherapeutenListe() results to ContactCard components
+
+- [ ] **Step 4: Create combined contact form**
+
+Create `src/features/kontakte/components/contact-form.tsx`:
+- Two sections separated by ``: "Therapeut:in" (name, stadt, telefon, email) and "Kontaktversuch" (datum, kanal select, ergebnis select, notiz textarea)
+- On submit: createTherapeut() then createKontakt() with returned ID, navigate to `/kontakte`
+- Cancel button navigates back
+
+- [ ] **Step 5: Create contact routes**
+
+Create `src/routes/kontakte/index.tsx` rendering ``.
+Create `src/routes/kontakte/neu.tsx` rendering ``.
+
+- [ ] **Step 6: Add bottom navigation to root layout**
+
+Update `src/routes/__root.tsx`: add a `