diff --git a/docs/scrum4me-architecture.md b/docs/scrum4me-architecture.md
index bc244f5..5197f99 100644
--- a/docs/scrum4me-architecture.md
+++ b/docs/scrum4me-architecture.md
@@ -157,6 +157,8 @@ Scrum4Me is een desktop-first Next.js 16 webapplicatie die server-side wordt ger
**Indexes:** `(pbi_id, priority, sort_order)`, `(sprint_id, sort_order)`, `(product_id, status)`
+**Auto-promotie/demotie via task-status:** zodra alle tasks van een story op `DONE` staan en de huidige story-status nog niet `DONE` is, promoot dezelfde transactie de story naar `DONE`. Wordt een task van een `DONE`-story uit `DONE` getrokken (heropening), dan demoot de story terug naar `IN_SPRINT` — niet naar `OPEN`, want `OPEN` betekent "terug in productbacklog" en is een sprint-management-actie. De logica zit in [lib/tasks-status-update.ts](../lib/tasks-status-update.ts) en wordt aangeroepen door alle drie de task-status-write-paden (`updateTaskStatusAction`, `saveTask` edit-mode, REST `PATCH /api/tasks/[id]`).
+
---
### `story_logs`
diff --git a/docs/scrum4me-task-dialog.md b/docs/scrum4me-task-dialog.md
new file mode 100644
index 0000000..fa676bd
--- /dev/null
+++ b/docs/scrum4me-task-dialog.md
@@ -0,0 +1,506 @@
+# Scrum4Me — TaskDialog Spec
+
+> Volledige design-spec voor de add/update task dialog van de inspannings monitor app.
+> Resultaat van een grill-me sessie (15 vragen, alle beslissingen vastgelegd).
+
+---
+
+## Stack
+
+- **Framework:** Next.js (App Router)
+- **ORM:** Prisma
+- **UI components:** shadcn/ui — wrappers rond `@base-ui/react` (zoals expliciet vastgelegd in `CLAUDE.md`)
+- **Styling:** Tailwind CSS
+- **Form:** react-hook-form + @hookform/resolvers/zod
+- **Design language:** Material Design 3 als theming-laag (geen MUI components)
+- **Theming:** `material-color-utilities` voor dynamic color, `next-themes` voor dark mode
+- **Icons:** Lucide
+- **Markdown rendering:** `react-markdown` + `remark-gfm`
+- **Toasts:** sonner (shadcn default)
+
+> **Composition-regel:** dit project gebruikt `@base-ui/react`, niet Radix. Composition gebeurt via de **`render`-prop**, niet via `asChild`. Zie ook `CLAUDE.md` "UI Library Conventions".
+>
+> ```tsx
+> // ✅ goed
+> }>...
+> // ❌ fout — geeft TS-errors
+>
+> ```
+
+> **Dialog-primitive:** bouw de TaskDialog op de bestaande wrapper in `components/ui/dialog.tsx` (shadcn rond `@base-ui/react`). **Geen** directe imports uit `@base-ui/react` voor dialog-primitives in deze feature — anders krijg je twee parallelle dialog-implementaties die uit de pas gaan lopen qua animatie, focus-trap en theming.
+
+---
+
+## Dependency-impact
+
+De volgende packages staan **nog niet** in `package.json` en moeten direct als runtime-`dependencies` worden toegevoegd voordat de eerste commit van deze feature gemerged wordt (CLAUDE.md "Dependencies"-regel). Voeg ze in dezelfde change toe waarin ze geïmporteerd worden, en vermeld ze in de docs-sync.
+
+| Package | Doel | Scope |
+|---|---|---|
+| `react-hook-form` | form-state management voor TaskDialog | runtime |
+| `@hookform/resolvers` | zod-resolver voor `react-hook-form` | runtime |
+| `react-textarea-autosize` | auto-grow textareas voor `description` / `implementation_plan` | runtime |
+| `react-markdown` | markdown rendering elders in de app (taakdetail, hover-card) | runtime |
+| `remark-gfm` | GFM-extensies (tabellen, taken, strikethrough) | runtime |
+| `@tailwindcss/typography` | `prose`-classes voor markdown-styling | runtime (Tailwind v4 plugin) |
+
+**Bewust niet meegenomen:**
+
+- `material-color-utilities` — dynamic color valt buiten v1 (zie Theming hieronder).
+- `nuqs` — start met **native `searchParams`**; als de URL-state-handling te omslachtig wordt, dan pas `nuqs` als losse refactor-task introduceren. Niet in deze feature mengen.
+
+Reeds aanwezig en gebruikt: `@base-ui/react`, `next-themes`, `lucide-react`, `sonner`, `zod`, `prisma`.
+
+---
+
+## Component-API
+
+Eén component `TaskDialog`, mode afgeleid uit `task?: Task` prop:
+
+```tsx
+ // task undefined = create mode, task aanwezig = edit mode
+```
+
+Open/close-state komt uit de URL via `nuqs` of `searchParams`. Taken leven binnen de context van een sprint of een PBI/story — er is **geen** zelfstandige `/tasks`-route:
+
+```
+/sprint/?newTask=1 → create-dialog open binnen sprint-context
+/sprint/?editTask= → edit-dialog open binnen sprint-context
+/products//backlog?newTask=1 → create-dialog open binnen backlog-context
+/products//backlog?editTask=
+```
+
+Dialog sluit door dezelfde route opnieuw te pushen zonder de `newTask` / `editTask` query-params (bv. `router.push(\`/sprint/\${sprintId}\`)`).
+
+---
+
+## Velden die de dialog gebruikt
+
+De dialog leest en schrijft uitsluitend deze velden van het `Task`-record. Het volledige datamodel valt buiten scope van deze spec.
+
+| Veld | Type | Mode |
+|---|---|---|
+| `title` | `string` (required) | beide |
+| `description` | `string \| null` | beide |
+| `implementation_plan` | `string \| null` | beide |
+| `priority` | `int` (1-4, P1 = hoogste) | beide |
+| `status` | `TaskStatus` enum | alleen edit (default `TO_DO` op create, niet getoond) |
+| `created_at` | `Date` | alleen edit, read-only metadata in header |
+
+`TaskStatus` enum-waarden: `TO_DO | IN_PROGRESS | REVIEW | DONE`.
+
+---
+
+## Layout & responsive gedrag
+
+| Breakpoint | Breedte | Hoogte |
+|---|---|---|
+| Mobiel (<640px) | full-screen | full-screen |
+| Tablet (640-1024px) | `90vw` | `max-h-[85vh]` |
+| Desktop (≥1024px) | `max-w-[50vw]`, `min-w-[480px]` | `max-h-[85vh]` |
+
+- Padding: `p-6` rondom
+- Veld-spacing binnen blok: `space-y-6` (24px)
+- Sticky header (titel + close) en sticky footer (knoppen)
+- Body scrollt als content de `max-h` overschrijdt
+- Footer heeft top-border in `outline-variant` kleur
+
+---
+
+## Velden
+
+In volgorde van boven naar beneden:
+
+| Veld | Control | Mode | Validatie |
+|---|---|---|---|
+| `title` | `Input` (single-line) | beide | required, trim, 1-120 chars |
+| `description` | `Textarea` (auto-grow, 3-6 regels) | beide | optional, max 2.000 chars, markdown |
+| `implementation_plan` | `Textarea` (auto-grow, 5-12 regels) | beide | optional, max 10.000 chars, markdown |
+| `priority` | Segmented buttons (P1/P2/P3/P4) | beide | int 1-4, default 3 |
+| `status` | `Select` met gekleurde dot | alleen edit | enum, default TO_DO |
+
+Verberg `status` in create-mode (default = TO_DO is genoeg).
+
+### Auto-grow textareas
+Gebruik `react-textarea-autosize`. Bereikt het veld zijn max-regels, dan `overflow-y-auto` (interne scroll). De **dialog-body** scrollt onafhankelijk; je krijgt zelden geneste scrolls.
+
+### Karakter-counter
+Alleen tonen vanaf 75% van de limiet. Klein, rechtsonder in het veld, `muted-foreground` kleur. Bv. `1547 / 2000`.
+
+### Markdown hint
+Onder elk textarea: `Markdown ondersteund (lijstjes, **vet**, \`code\`)` — klein, muted.
+
+### Priority segmented buttons
+```
+[ P1 Critical ] [ P2 High ] [ P3 Medium ] [ P4 Low ]
+ error tertiary primary outline
+```
+- Lager getal = hoger prio (industriestandaard, Linear/Jira-conform)
+- Default geselecteerd: P3 Medium
+- Geen 0-waarde toestaan
+
+### Status select (alleen edit)
+- TO_DO — grijze dot
+- IN_PROGRESS — blauwe dot
+- REVIEW — paarse dot
+- DONE — groene dot
+
+### `created_at` als header-metadata
+In edit-mode tonen in de dialog-header naast de titel:
+
+```
+Taak bewerken Aangemaakt: 23 apr 2026
+```
+
+Klein, `muted-foreground`, niet als form-veld.
+
+---
+
+## Validatie
+
+- **Gedeeld zod-schema** in `lib/schemas/task.ts`, geïmporteerd door zowel form als server action
+- react-hook-form mode: `onTouched` (eerste validatie bij blur, daarna onChange)
+- Errors onder het veld, in error-color, met label en outline van het veld in dezelfde kleur
+- Geen toasts voor field-level errors
+- Submit-button blijft enabled bij errors — klik scrollt naar eerste error-veld + focus
+
+```ts
+// lib/schemas/task.ts (richtlijn)
+export const taskSchema = z.object({
+ title: z.string().trim().min(1, "Verplicht").max(120),
+ description: z.string().max(2000).optional(),
+ implementation_plan: z.string().max(10000).optional(),
+ priority: z.number().int().min(1).max(4),
+ status: z.nativeEnum(TaskStatus).optional(), // alleen in edit
+});
+```
+
+---
+
+## Submission
+
+### Auth-scoping (verplicht)
+
+Elke server action — zowel `saveTask` als `deleteTask` — moet de operatie scope-en op de huidige user. Cross-tenant writes voorkomen via `productAccessFilter(userId)` (of het project-equivalent), zodat een user geen task kan schrijven of verwijderen die niet onder zijn product-scope valt.
+
+> Concreet: de Prisma-mutatie staat nóóit alleen op `where: { id: taskId }`. De scope wordt verplicht gecombineerd in elke `update`/`delete`/`create`-call.
+
+### Demo read-only enforcement (drie lagen — ST-1110)
+
+Elke write-flow moet door deze drie lagen:
+
+1. **Middleware-guard in `proxy.ts`** — blokkeert demo-sessies op write-routes vóór de server action überhaupt loopt. Returnt **403**.
+2. **`session.isDemo`-check in de server action zelf** — defense-in-depth voor het geval een write-flow buiten een proxy-route loopt (bv. directe action-invocation). Returnt **403**.
+3. **`` op de save- en delete-knoppen** — UI-laag: knoppen zijn zichtbaar disabled met tooltip "Demo-modus: opslaan uitgeschakeld". Vermijdt onnodige round-trips.
+
+### Server Action
+
+```ts
+// app/actions/tasks.ts
+"use server"
+
+export async function saveTask(
+ input: TaskInput,
+ context: { sprintId?: string; productId?: string }, // voor revalidatePath en scope
+): Promise {
+ const session = await getSession();
+ if (session.isDemo) return { ok: false, code: 403, error: "demo_readonly" };
+
+ const scope = await productAccessFilter(session.userId); // verplicht
+ // ... validate met taskSchema → Prisma write binnen `scope`
+}
+
+type SaveTaskResult =
+ | { ok: true; task: Task }
+ | { ok: false; code: 422; error: "validation"; fieldErrors: Record }
+ | { ok: false; code: 403; error: "demo_readonly" | "forbidden" }
+ | { ok: false; code: 500; error: "server_error" }
+```
+
+### Foutcodes (volgens `CLAUDE.md` "Foutcodes API")
+
+| Code | Wanneer | UI-respons |
+|---|---|---|
+| **422** | zod-validatiefout (server-side dubbelcheck) | `fieldErrors` mappen naar `form.setError()`, geen toast |
+| **403** | demo-sessie probeert te schrijven, of cross-tenant write geblokkeerd | toast "Niet toegestaan in demo-modus" / "Geen toegang", form blijft open |
+| **500** | onverwachte serverfout | toast met "Opnieuw proberen"-knop, form-state behouden |
+
+> Field-level errors zijn **alleen** geldig bij `code: 422`. Bij andere codes is `fieldErrors` ongedefinieerd.
+
+### Revalidation
+
+`revalidatePath` op de **context-route** waarin de dialog werd geopend, niet op een statische `/tasks`-path:
+
+```ts
+if (context.sprintId) revalidatePath(`/sprint/${context.sprintId}`);
+if (context.productId) revalidatePath(`/products/${context.productId}/backlog`);
+```
+
+De aanroepende client geeft de relevante `sprintId` of `productId` mee als argument bij elke save/delete. Geen hard-coded paths in de action zelf.
+
+### Flow
+
+- Synchroon (geen optimistic update in v1)
+- Tijdens submit: cancel- en save-knop disabled, spinner in save-knop met "Opslaan...", velden blijven enabled
+- Server saniteert en valideert opnieuw met hetzelfde zod-schema
+- Field-level server errors (bv. unique constraint op title binnen scope) → `code: 422` met `fieldErrors`, terugmappen naar `form.setError()`
+
+### Error handling
+
+- **422** → field errors inline tonen, geen toast
+- **403** → toast met passende boodschap, form blijft open, ingevulde waarden behouden
+- **500 / netwerk** → toast met "Opnieuw proberen"-knop, form-state behouden, knoppen weer enabled
+
+---
+
+## Dialog-gedrag
+
+### Sluiten met dirty state
+- Form niet aangeraakt → Esc / backdrop-klik / Cancel sluiten direct
+- Form `isDirty` → Esc / backdrop-klik / Cancel triggeren `AlertDialog`: *"Wijzigingen niet opgeslagen — weggooien?"*
+
+### Keyboard shortcuts
+- **Esc** — sluit (met dirty-check)
+- **Cmd/Ctrl+Enter** — submit vanuit elk veld
+- **Enter in title-input** — submit niet (alleen Cmd/Ctrl+Enter)
+- **Enter in textarea** — newline (default browser behavior, niet overriden)
+- **Tab** — title → description → implementation_plan → priority → (status) → cancel → save
+
+### Focus management
+- Bij openen: focus op `title`-input
+- Edit-mode: cursor aan einde van bestaande titel, **geen auto-select** (anders typt user per ongeluk de titel weg)
+- Bij sluiten: focus terug naar het element dat de dialog opende (`@base-ui/react` doet dit by default — niet breken)
+- Bij submit-error: focus naar eerste error-veld
+
+### Motion
+MD3-conform:
+- Open: 250ms, easing `cubic-bezier(0.2, 0, 0, 1)`, scale 0.95→1 + opacity 0→1
+- Close: 200ms, easing `cubic-bezier(0.4, 0, 1, 1)`
+
+### Backdrop
+Scrim `rgba(0,0,0,0.4)` (iets sterker dan MD3-default 0.32 voor betere contrast op licht/donker).
+
+---
+
+## Footer
+
+### Edit-mode
+```
+[ Verwijderen ] [ Annuleren ] [ Opslaan ]
+ tonal (error-container) text filled (primary)
+```
+
+### Create-mode
+```
+ [ Annuleren ] [ Aanmaken ]
+ text filled (primary)
+```
+
+### Delete-flow
+- Klik op "Verwijderen" → `AlertDialog`: *"Weet je zeker? Dit kan niet ongedaan worden."*
+- Bevestigen → `deleteTask` server action (zelfde auth-scoping en demo-checks als `saveTask`) → `revalidatePath` op de context-route (`/sprint/` of `/products//backlog`) → dialog sluit → toast "Taak verwijderd"
+- Geen undo in v1
+
+---
+
+## Triggers (hoe komt de user erbij?)
+
+De dialog wordt vanuit twee context-pagina's geopend: een sprint-detail (`/sprint/`) of een product-backlog (`/products//backlog`).
+
+> **Vervangt bestaande create/edit-flows.** Deze TaskDialog is de **enige** flow voor het aanmaken en bewerken van taken in beide contexten. Bestaande inline-edit-paden in `components/sprint/task-list.tsx` (en eventueel in de backlog) worden door deze dialog vervangen — niet er naast geplaatst. De huidige task-row-rendering wordt aangepast om bij klik de dialog te openen via `?editTask=`; geen aparte edit-icon, geen inline form. Een eventuele "+ Nieuwe taak"-knop in de bestaande tasklist-header wordt eveneens omgeleid naar `?newTask=1` op dezelfde route.
+
+- **Create:** filled button `+ Nieuwe taak` rechtsboven in de tasklist-header van de huidige context (FAB op mobiel optioneel later). Klik zet de juiste query-param (`?newTask=1`) op de huidige route.
+- **Edit:** klik op de hele rij in de tasklist (geen apart edit-icoon). Klik zet `?editTask=` op de huidige route.
+- **Loading edit-mode:** Suspense met minimale skeleton (3 grijze balken voor inputs), `200ms` delay zodat snelle fetches geen flicker tonen
+
+### Server-fetch
+Bij `?editTask=`: server component fetcht de taak vóór render — **inclusief auth-scoping** via `productAccessFilter(userId)` zodat een user nooit een task uit een ander product kan openen via een geraden ID. Bestaat de taak niet of valt 'm buiten scope → toast + redirect naar de context-route zonder query-param (bv. `/sprint/`).
+
+---
+
+## Theming (Material Design 3 tokens)
+
+> **Bron-of-truth in v1:** de bestaande **statische** tokens in `app/styles/theme.css` zijn canoniek. De TaskDialog **consumeert** deze tokens en voegt er geen nieuwe aan toe. Dynamic color (`material-color-utilities`) valt **buiten v1** — niet introduceren in deze feature.
+
+### Color
+- TaskDialog gebruikt de bestaande MD3-tokens uit `app/styles/theme.css`: `--primary`, `--on-primary`, `--surface-container`, `--surface-container-high`, `--surface-container-low`, `--error-container`, `--on-error-container`, `--outline-variant`, plus de project-specifieke `--status-*` en `--priority-*` tokens
+- Eventueel ontbrekende tokens (bv. een specifieke `surface-container-high` als die er nog niet is) worden in **dezelfde commit** als de feature aan `theme.css` toegevoegd, niet ad-hoc per component gehard-codeerd
+- **Verboden:** willekeurige Tailwind-kleuren (`bg-blue-500`, etc.). Altijd semantische tokens — zie `docs/scrum4me-styling.md`
+
+### Dark mode
+- `next-themes` is al in de stack; TaskDialog erft automatisch de actieve kleurmodus via de bestaande tokens
+- Geen extra setup nodig in deze feature
+
+### Surface elevation
+Hybrid (tonal surface + zachte shadow):
+- Dialog: `surface-container-high` background + `shadow-2xl` met getemperde opacity
+- Form inputs: `surface-container-low` background, geen shadow
+- Geen pure tonal-only (voelt te plat op desktop)
+
+### Buttons
+- **Filled** (Save/Aanmaken): `primary` background, `on-primary` tekst
+- **Text** (Cancel): geen background, `primary` tekst
+- **Tonal error** (Delete): `error-container` background, `on-error-container` tekst
+
+### Density
+Comfortable (geen compact):
+- Single-line input-hoogte: 56px (MD3 outlined text field default)
+- Veld-spacing: 24px (`space-y-6`)
+- Dialog-padding: 24px alle kanten (`p-6`)
+
+### Typography
+- **Font:** Inter via `next/font/google` (geen Roboto-dwang)
+- **Schaal (beperkt):**
+ - `headline-small` (24px) — dialog-titel
+ - `body-large` (16px) — form-input tekst
+ - `body-medium` (14px) — helptext, counter
+- Geen Material-specifieke letter-spacing tweaks; Inter-defaults voldoen
+
+### Iconen
+Lucide (shadcn default). Geen Material Symbols importeren — ~150kb winst en visueel neutraal genoeg om in MD3-themed app te passen.
+
+---
+
+## Markdown rendering (buiten de dialog)
+
+Voor weergave van `description` en `implementation_plan` elders in de app (taakdetail, hover-card, etc.):
+
+```tsx
+import ReactMarkdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+
+
+ {content}
+
+```
+
+- Tailwind Typography (`prose prose-sm`) voor styling
+- `remark-gfm` voor tabellen, taken, strikethrough
+- `react-markdown` saniteert by default; `disallowedElements` als extra defense-in-depth
+
+---
+
+## Hergebruik & generalisatie
+
+De TaskDialog is de eerste van naar verwachting meerdere entity-dialogs (PBI, Story, Todo volgen logisch). Bouw daarom **vanaf dag 1** een dunne scheiding tussen generic shell+primitives en entity-specifieke form-body. Dit is geen speculatieve abstractie: de breuklijn tussen "dialog-mechanica" en "welke velden horen bij deze entiteit" is natuurlijk en levert per nieuwe entiteit ~70% codebesparing op.
+
+### Wat generic wordt (`components/entity-dialog/`)
+
+| Component | Waarom generic |
+|---|---|
+| `entity-dialog.tsx` | Shell: sticky header/footer, responsive layout, motion, backdrop, dirty-close-guard, keyboard-shortcuts, focus-management. Slot-props voor body en footer-actions. |
+| `priority-segmented.tsx` | P1-P4 segmented buttons; `priority: int 1-4` is identiek over Task / PBI / Story / Todo. |
+| `auto-grow-textarea.tsx` | Wrapper rond `react-textarea-autosize` met char-counter (vanaf 75%) en markdown-hint. Generic — neemt min/max regels en max-chars als props. |
+| `dirty-close-guard.tsx` | AlertDialog "Wijzigingen niet opgeslagen — weggooien?" — entity-agnostisch. |
+
+Deze primitives importeren **alleen** uit `components/ui/*` en hebben geen kennis van Task / Story / PBI.
+
+### Wat entity-specifiek blijft (`components/tasks/`)
+
+| Component | Waarom niet generic |
+|---|---|
+| `task-dialog.tsx` | Dunne wrapper: kiest body, koppelt `saveTask`/`deleteTask`, levert label-strings ("Taak bewerken" / "Aangemaakt: …"). Geen mechanica meer in dit bestand. |
+| `task-form.tsx` | Velden zijn task-specifiek (`title`, `description`, `implementation_plan`, `priority`, `status`). Andere entiteiten (Story heeft `acceptance_criteria`, PBI heeft alleen `description`) krijgen elk hun eigen `*-form.tsx`. |
+| `task-status-select.tsx` | `TaskStatus` enum met 4 specifieke waarden + dot-kleurmapping. `StoryStatus` (`OPEN | IN_SPRINT | DONE`) en `PbiStatus` (`OPEN | IN_SPRINT | DONE` + `BLOCKED`) hebben andere enums en horen bij eigen select-componenten. |
+
+### Wat **niet** abstraheren in v1
+
+- **URL-state pattern** — `?newTask=1` / `?editTask=` per route. Een toekomstige PBI-dialog krijgt `?newPbi=1` / `?editPbi=` op zijn eigen routes. Copy-paste tussen 2-3 pages is goedkoper dan een generic helper die je later toch moet generaliseren.
+- **Save/delete-flows** — auth-scoping, demo-checks en revalidatePath verschillen subtiel per entiteit (verschillende productAccessFilter-paden, verschillende context-routes). Per entiteit een eigen actions-file in `app/actions/.ts`.
+
+### Per-entiteit kostenplaatje
+
+Wanneer er straks een PbiDialog of StoryDialog gebouwd wordt, kost dat alleen:
+
+1. `components//-form.tsx` — de velden + zod-schema
+2. `components//-status-select.tsx` — als de entiteit een status-veld heeft
+3. `components//-dialog.tsx` — dunne wrapper rond `EntityDialog` met de juiste form en save/delete-handler
+4. `app/actions/.ts` — server actions
+5. URL-state uitbreiding op de relevante page(s)
+
+Geen herhaling van layout, motion, dirty-check, keyboard-shortcuts, of segmented/textarea-primitives.
+
+---
+
+## Bewust NIET in v1
+
+Om scope te bewaken:
+
+- ❌ Bulk-edit (meerdere taken tegelijk)
+- ❌ Drag-and-drop herorderen
+- ❌ Sub-tasks / parent-child relaties
+- ❌ Tags / labels / categorieën
+- ❌ Due dates / reminders
+- ❌ Attachments / file uploads
+- ❌ Comments / activity log
+- ❌ Sharing / collaboration
+- ❌ Undo na delete (toast met undo-actie)
+- ❌ Cmd+K keyboard-driven creation zonder dialog
+- ❌ Templates voor terugkerende taken
+- ❌ Time tracking (uren-registratie) — wel relevant voor inspannings-monitor, maar apart feature
+- ❌ Telemetrie / analytics
+- ❌ Optimistic locking — niet geïmplementeerd in v1 (last-write-wins binnen scope)
+- ❌ Tabs voor secties — alleen spacing-gebaseerde groepering
+- ❌ Section-headers — implicit via spacing, geen labels
+
+> Heroverweeg deze keuzes pas als de app groeit. Niet om je te beperken, maar om elke "ja maar moeten we niet ook…"-impuls een bewuste afweging te maken.
+
+---
+
+## File structuur (richtlijn)
+
+```
+app/
+├── sprint/
+│ └── [id]/
+│ └── page.tsx # leest searchParams, rendert TaskDialog
+├── products/
+│ └── [id]/
+│ └── backlog/
+│ └── page.tsx # leest searchParams, rendert TaskDialog
+├── actions/
+│ └── tasks.ts # saveTask, deleteTask server actions (auth-scoped)
+components/
+├── ui/
+│ ├── dialog.tsx # bestaande @base-ui/react-wrapper
+│ └── demo-tooltip.tsx # wrapper voor save/delete-knoppen in demo-mode
+├── entity-dialog/ # GENERIC — geen kennis van Task/Story/PBI
+│ ├── entity-dialog.tsx # shell: header/footer/motion/dirty-check/keyboard
+│ ├── priority-segmented.tsx # P1-P4 segmented buttons
+│ ├── auto-grow-textarea.tsx # textarea met counter + markdown-hint
+│ └── dirty-close-guard.tsx # AlertDialog bij dirty close
+├── tasks/ # ENTITY-SPECIFIEK
+│ ├── task-dialog.tsx # dunne wrapper rond EntityDialog
+│ ├── task-form.tsx # task-velden + react-hook-form binding
+│ └── task-status-select.tsx # TaskStatus enum + dot-kleuren
+lib/
+├── schemas/
+│ └── task.ts # gedeeld zod-schema (form + server action)
+├── auth/
+│ └── product-access-filter.ts # scope-helper, gedeeld door page-fetches en actions
+proxy.ts # demo-readonly middleware-guard (laag 1 van 3)
+```
+
+---
+
+## Implementatie-volgorde (suggestie)
+
+1. Dependencies toevoegen aan `package.json` (zie "Dependency-impact"); commit als `chore(ST-XXX): add deps for task dialog`
+2. zod-schema in `lib/schemas/task.ts`
+3. `productAccessFilter` helper checken/uitbreiden in `lib/auth/`
+4. Server actions (`saveTask`, `deleteTask`) met **auth-scoping én demo-check** (laag 2) — testen via thunk
+5. `proxy.ts` middleware-guard voor demo-routes (laag 1) — alleen als nog niet aanwezig voor deze routes
+6. Eventueel ontbrekende MD3-tokens aanvullen in `app/styles/theme.css` (geen dynamic color in v1)
+7. ``-wrapper component (laag 3)
+8. TaskDialog — create-mode eerst (minder edge cases), bovenop bestaande `components/ui/dialog.tsx`-wrapper
+9. Edit-mode toevoegen (status field, delete-knop, `created_at`-metadata)
+10. URL-state via native `searchParams` binnen sprint en backlog routes (geen `nuqs` in v1)
+11. **Bestaande task-row / tasklist-trigger refactoren** — `components/sprint/task-list.tsx` (en backlog-equivalent) klikbaar maken zodat ze de dialog openen via query-param; oude inline-edit-paden verwijderen
+12. Suspense + skeleton voor edit-mode loading + scope-check op fetch
+13. Dirty-check + AlertDialog
+14. Keyboard shortcuts (Cmd+Enter)
+15. Markdown rendering elders (out-of-scope voor dialog zelf, maar related)