From d8b677214869259a93f5750e95c0676e7591d752 Mon Sep 17 00:00:00 2001 From: Akeshan <59804062+cyberAkeshan@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:35:12 +0200 Subject: [PATCH] Interim-UI-Handover: 55 Vorschau-Screens fuer David (AD-28) Uebergabe der Dev-Interim-Screens aus HiveGrid-Cloud an den Wireframes-Vault. INDEX + 7 Domaenen-Seiten (CRM, Leads, HR, Files/Tasks, Audit/Notifications, Integrations, Auth/RBAC) mit je Screen: D-*-Ziel, Archetyp, Route, VM, benoetigte ~/ui-Elemente, Actions, States. Layout bleibt Davids freie Hand. Co-Authored-By: Claude Opus 4.8 (1M context) --- Tickets/Interim-Handover/INDEX.md | 101 +++++++ Tickets/Interim-Handover/P02-Auth-RBAC.md | 53 ++++ .../P03-Audit-Notifications.md | 74 +++++ Tickets/Interim-Handover/P04-Files-Tasks.md | 87 ++++++ Tickets/Interim-Handover/P05-Integrations.md | 82 +++++ Tickets/Interim-Handover/P06-CRM.md | 83 ++++++ Tickets/Interim-Handover/P07-Leads.md | 83 ++++++ Tickets/Interim-Handover/P17-HR.md | 279 ++++++++++++++++++ 8 files changed, 842 insertions(+) create mode 100644 Tickets/Interim-Handover/INDEX.md create mode 100644 Tickets/Interim-Handover/P02-Auth-RBAC.md create mode 100644 Tickets/Interim-Handover/P03-Audit-Notifications.md create mode 100644 Tickets/Interim-Handover/P04-Files-Tasks.md create mode 100644 Tickets/Interim-Handover/P05-Integrations.md create mode 100644 Tickets/Interim-Handover/P06-CRM.md create mode 100644 Tickets/Interim-Handover/P07-Leads.md create mode 100644 Tickets/Interim-Handover/P17-HR.md diff --git a/Tickets/Interim-Handover/INDEX.md b/Tickets/Interim-Handover/INDEX.md new file mode 100644 index 00000000..ead9aac2 --- /dev/null +++ b/Tickets/Interim-Handover/INDEX.md @@ -0,0 +1,101 @@ +# Interim-UI-Handover → David (AD-28) + +**Stand:** 2026-07-08 · **Quelle:** HiveGrid-Cloud, `apps/web/app/components/screens/` · **Owner:** Akeshan + +## Was ist das? + +Der Dev-Strang hat nach **AD-28** (`.planning/INTERIM-UI-PLAYBOOK.md`) für UI-lastige Tickets **Interim-UI- +Vorschau-Screens** gebaut — lauffähige `Page`-Komponenten aus `~/ui` (VM als Prop), damit Backend/Loader/ +Action getestet sind, **bevor** deine finalen Subframe-Screens (`D-*`) stehen. Dieses Verzeichnis ist die +Übergabe dieser Interim-Screens an dich, damit dein Agent jeden Screen dem richtigen `D-*`-Paket + Archetyp +zuordnen und die finale Optik bauen kann. + +**Zwei Dinge sind wichtig:** +1. **Layout ist deine freie Hand** (Designer-First). Die Detailseiten listen nur *benötigte Elemente / Daten / + Aktionen / States* — **keine** Anordnung. Du überschreibst Spec/UX in der „Davids Delta"-Sektion je Screen. +2. Die Interim-Screens sind **lauffähige Referenz**: die `.tsx`-Pfade zeigen in den gebundelten Checkout unter + `03 Quellen und Referenzen/hivegrid-cloud/` — du kannst jeden Screen real anschauen. Die stabile Grenze ist + das **View-Model (VM) + der Loader**; dein Subframe-Screen ersetzt nur das `Page`-Innere (Component-Swap, + kein Neubau). Danach: `🎨 design-nachzug` in der Ticket-Doc auf `erledigt`. + +## Domänen-Übersicht + +| Seite | Domäne | Bau-Ticket | D-*-Ziel | Screens | Status | +|---|---|---|---|---|---| +| [P06-CRM](P06-CRM.md) | CRM-Kern | R-P6 | D-CRM | 6 | offen | +| [P07-Leads](P07-Leads.md) | Lead-Inbox | R-P7 | D-P7 | 6 | offen | +| [P17-HR](P17-HR.md) | HR-Hub | AK-P17a/b | D-HR | 21 | offen | +| [P04-Files-Tasks](P04-Files-Tasks.md) | Files & Tasks | AK-P4a/b | D-P4 | 6 | offen | +| [P03-Audit-Notifications](P03-Audit-Notifications.md) | Audit & Notifications | AK-P3 | D-P3 | 5 | offen | +| [P05-Integrations](P05-Integrations.md) | Integrations/Apps-Engine | R-P5 | D-P5 | 6 | offen | +| [P02-Auth-RBAC](P02-Auth-RBAC.md) | Auth & RBAC | R-P2a/b | D-P2 | 5 | ✅ David fertig (nur Abgleich) | + +**Gesamt: 55 Interim-Screens** über 10 Domänen-Ordner. Archetyp-Codes (A1–A7) stammen aus den Header- +Kommentaren der Interim-Screens bzw. der `D-*`-Screen-Tabelle; bei Unklarheit gilt deine `D0`-Archetypen-Doku. + +## Screen-Index (alle 55) + +| Screen | D-*-Ziel | Archetyp | Route | Status | +|---|---|---|---|---| +| KontaktListPage | D-CRM | A2 | /app/:tenant/contacts (+ /contacts/companies) | offen | +| KontaktDetailPage | D-CRM | A3 | /app/:tenant/contacts/:id (+ /contacts/companies/:id) | offen | +| KontaktNewPage | D-CRM | A4 | /app/:tenant/contacts/new | offen | +| CustomFieldAdminPage | D-CRM | A2 | /app/:tenant/contacts/admin/custom-fields | offen | +| TagAdminPage | D-CRM | A2 | /app/:tenant/contacts/admin/tags | offen | +| DuplicateToolPage | D-CRM | A5 | /app/:tenant/contacts/admin/duplicates | offen | +| LeadInboxPage | D-P7 | A2 | /:tenant/leads | offen | +| LeadDetailPage | D-P7 | A3 | /:tenant/leads/:id | offen | +| LeadNewPage | D-P7 | A4 | /:tenant/leads/new | offen | +| LeadStatisticsPage | D-P7 | A2 | /:tenant/leads/statistics | offen | +| LeadSourcesAdminPage | D-P7 | A6 | /:tenant/admin/lead-sources | offen | +| LeadAntiSpamAdminPage | D-P7 | A6 | /:tenant/admin/lead-anti-spam | offen | +| HrHubPage | D-HR | A1/A6 | /:tenant/hr | offen | +| HrDirectoryPage | D-HR | A2 | /:tenant/hr/directory | offen | +| HrDirectoryDetailPage | D-HR | A3 | /:tenant/hr/directory/:userId | offen | +| HrLeavePage | D-HR | A2+A4/A5 | /:tenant/hr/leave | offen | +| HrSickPage | D-HR | A2+A4 | /:tenant/hr/sick | offen | +| HrRequestsPage | D-HR | A2+A4/A5 | /:tenant/hr/requests | offen | +| HrPayslipsPage | D-HR | A2 | /:tenant/hr/payslips | offen | +| HrMyDocumentsPage | D-HR | A2 | /:tenant/hr/my-documents | offen | +| HrAnnouncementsFeedPage | D-HR | A2 | /:tenant/hr/announcements | offen | +| HrAnnouncementDetailPage | D-HR | A3 | /:tenant/hr/announcements/:id | offen | +| HrLocationsPage | D-HR | A2+A5 | /:tenant/hr/locations | offen | +| HrPasswordSafeListPage | D-HR | A2+A5 | /:tenant/hr/password-safe | offen | +| HrVaultDetailPage | D-HR | A3+reveal | /:tenant/hr/password-safe/:vaultId | offen | +| HrSurveysListPage | D-HR | A2+A5 | /:tenant/hr/surveys | offen | +| HrSurveyParticipationPage | D-HR | A3/A4 | /:tenant/hr/surveys/:id | offen | +| HrSurveyResultsPage | D-HR | A3 | /:tenant/hr/surveys/:id/results | offen | +| HrAdminAnnouncementsPage | D-HR | A2+A4/A5 | /:tenant/hr/admin/announcements | offen | +| HrAdminPasswordSafePage | D-HR | A2/A6 | /:tenant/hr/admin/password-safe | offen | +| HrAdminPayslipsPage | D-HR | A2+A4 | /:tenant/hr/admin/payslips | offen | +| HrAdminRequestTemplatesPage | D-HR | A2+A5 | /:tenant/hr/admin/request-templates | offen | +| HrAdminUploadRemindersPage | D-HR | A2+A5 | /:tenant/hr/admin/upload-reminders | offen | +| FilesBrowserPage | D-P4 | A2 | /:tenant/files | offen | +| FileUploadPage | D-P4 | A4 | /:tenant/files/upload | offen | +| FileDetailPage | D-P4 | A3 | /:tenant/files/:id | offen | +| TasksListPage | D-P4 | A2 | /:tenant/tasks | offen | +| TaskDetailPage | D-P4 | A3+A5 | /:tenant/tasks/:id | offen | +| TaskNewPage | D-P4 | A5 | /:tenant/tasks/new | offen | +| AuditListPage | D-P3 | A2 | /admin/audit | offen | +| AuditExportPage | D-P3 | A2 | /admin/audit-export | offen | +| NotificationCenterPage | D-P3 | A2 | /app/:tenant/notifications | offen | +| NotificationsDrawer | D-P3 | A2 | n/a (globaler Drawer) | offen | +| NotificationSettingsPage | D-P3 | A6 | /app/:tenant/settings/notifications | offen | +| IntegrationsCatalogPage | D-P5 | A6 | /:tenant/integrations | offen | +| ConnectionDetailPage | D-P5 | A6+A2 | /:tenant/integrations/:app | offen | +| ConnectionCard | D-P5 | (sub) | n/a (Sub von ConnectionDetailPage) | offen | +| ConnectWizardDialog | D-P5 | A5+A6 | n/a (Sub von ConnectionDetailPage) | offen | +| SyncHistoryPage | D-P5 | A2 | /:tenant/integrations/:app/:connectionId/syncs | offen | +| SyncRunDetailPage | D-P5 | A3 | /:tenant/integrations/:app/:connectionId/syncs/:runId | offen | +| CreateWorkspacePage | D-P2 | A3 | /new-account | David fertig | +| MembersPage | D-P2 | A1 | /app/:tenant/settings/members | David fertig | +| RolesPage | D-P2 | A1 | /app/:tenant/settings/roles-and-permissions/roles | David fertig | +| InviteMemberForm | D-P2 | A4 | n/a (Sub von MembersPage) | David fertig | +| GroupMembersDialog | D-P2 | A4 | n/a (Sub von GroupsRoute) | David fertig | + +## Verweise + +- Interim-UI-Regeln: `03 Quellen und Referenzen/hivegrid-cloud/.planning/INTERIM-UI-PLAYBOOK.md` +- Design-Standards (Spacing/Dialog/Archetypen): `.../hivegrid-cloud/.planning/INTERIM-UI-DESIGN-STANDARDS.md` +- Phasen-Briefs (dein bisheriger Intake, jetzt mit Pointer hierher): `.../hivegrid-cloud/.planning/designer-briefs/PXX-*.md` +- Getrackte offene Design-Nachzüge im Dev-Repo: `grep -rl "design-nachzug: offen" .planning/ticket-docs/` diff --git a/Tickets/Interim-Handover/P02-Auth-RBAC.md b/Tickets/Interim-Handover/P02-Auth-RBAC.md new file mode 100644 index 00000000..3055017a --- /dev/null +++ b/Tickets/Interim-Handover/P02-Auth-RBAC.md @@ -0,0 +1,53 @@ +# P02 · Auth & RBAC (R-P2a/b → D-P2) + +> ℹ️ David-Delta liegt vor (`Tickets/Fertige Tickets/D-P2 Auth Wireframes.md`) — diese Seite dient dem +> Konsistenz-Abgleich, **kein neuer Design-Request**. Interim-UI-Referenz (AD-28), `~/ui`-komponiert, VM als Prop. + +## Auth + +### CreateWorkspacePage · D-P2 „Create-workspace (ohne Tenant)" · A3 Formular/Auth-Shell (kein A-Code im Header, inferiert) +- **Interim-Screen:** `apps/web/app/components/screens/auth/CreateWorkspacePage.tsx` +- **Route(n):** `/new-account` (`routes/new-account.tsx`) — nicht unter `app.$tenant/` (Pre-Tenant-Screen) +- **Daten (VM):** keine VM — reine Props `{ csrf, error?, backHref }` (route-injiziert; server-driven `/new-account`-Action erstellt den Tenant) +- **Benötigte UI-Elemente (KEIN Layout):** `~/ui`: Alert, Button, LinkButton, TextInput · Shell: `~/components/auth/AuthShell` (nicht `~/ui`) · Icons: FeatherArrowLeft +- **Interaktionen (Actions):** keine `…Actions` — natives `
` postet `name` + `csrf` (+ vestigiales `action=create`); Back-Link → `backHref` +- **States:** Error-Alert (nur idle-gated, `error`-Prop) · kein Empty/Loading · Permission-Denied n/a (Login-gated im Loader) +- **Davids Delta:** ✅ vorhanden (D-P2) — nur Abgleich + +## Users / RBAC + +### MembersPage · D-P2 „WF-P2 Admin RBAC" S01 Mitarbeiterliste (+ D01/M03/V01) · A1 Liste/Tabelle (kein A-Code im Header, inferiert) +- **Interim-Screen:** `apps/web/app/components/screens/users/MembersPage.tsx` +- **Route(n):** `/app/:tenant/settings/members` (`routes/app.$tenant/settings/members.tsx`) +- **Daten (VM):** `MembersListVM` — `packages/view-models/src/users/members-list.vm.ts` (+ Helfer `filterMembersRows`, `EMPTY_MEMBERS_FILTER`, Typen `MembersListRow`/`MembersListFilterState`/`MemberStatus`) +- **Benötigte UI-Elemente (KEIN Layout):** `~/ui`: Avatar, BadgeFeedback, Button, Checkbox, Dialog, DropdownMenu, EmptyState, IconButton, SearchInput, Select, Table, Tabs · Icons: FeatherChevronDown, FeatherMoreHorizontal, FeatherPlus, FeatherTrash, FeatherUserCheck, FeatherUserX (+ `SubframeCore.DropdownMenu`) +- **Interaktionen (Actions):** `MembersActions` — onToggleRole({userId, roleId, add}), onDeactivate({userId}), onReactivate({userId}), onDeleteInvitation({invitationId}), onInvite() +- **States:** Empty (`EmptyState`, `emptyHeadlineKey`/`emptyBodyKey` wenn keine Rows/Invites) · No-matches (`EmptyState`, `noMatchesKey`) · Loading (`vm.status !== "ready"` → leeres Gerüst) · M03 Deaktivieren-Confirm-Dialog · V01 Last-Admin-Blocker-Dialog · Permission-Denied → 404 im Loader (`verifyUserHasPermission`) +- **Davids Delta:** ✅ vorhanden (D-P2) — nur Abgleich + +### RolesPage · D-P2 „WF-P2 Admin RBAC" S02 Rollen & Berechtigungen (+ M04A/M04B/M02/D03) · A1 Liste/Tabelle + Editor-Dialog (kein A-Code im Header, inferiert) +- **Interim-Screen:** `apps/web/app/components/screens/users/RolesPage.tsx` +- **Route(n):** `/app/:tenant/settings/roles-and-permissions/roles` (`routes/app.$tenant/settings/roles-and-permissions/roles.tsx`) +- **Daten (VM):** `RolesListVM` — `packages/view-models/src/users/roles-list.vm.ts` (+ Editor-VM `buildRoleEditorVM`/`filterRoleEditorSections`/`togglePermission` + Typ `RoleEditorInputPermission` aus `packages/view-models/src/users/role-editor.vm.ts`; Helfer `filterRolesRows`, `EMPTY_ROLES_FILTER`, Typen `RolesListRow`/`RolesListFilterState`/`RoleTypeKey`) +- **Benötigte UI-Elemente (KEIN Layout):** `~/ui`: Alert, Button, Dialog, DropdownMenu, EmptyState, IconButton, SearchInput, Select, Switch, Table, Tabs, TextArea, TextInput · Icons: FeatherCopy, FeatherEdit2, FeatherMoreHorizontal, FeatherPlus, FeatherTrash (+ `SubframeCore.DropdownMenu`) +- **Interaktionen (Actions):** `RolesActions` — onCreate({name, description, permissionNames}), onUpdate(roleId, {name, description, permissionNames}), onDelete(roleId) +- **States:** Empty (`EmptyState`, `emptyHeadlineKey`/`emptyBodyKey` wenn keine Rollen) · No-matches (`EmptyState variant="no-results"`, `noMatchesKey`) · Loading (`vm.status !== "ready"` → leeres Gerüst) · M02-Editor No-matches (`EmptyState`) · D03 Delete-Blocker (`deleteBlockedInUse`) · Permission-Denied → 404 im Loader (`requireWorkspaceRolesBuilderAccess`) +- **Davids Delta:** ✅ vorhanden (D-P2) — nur Abgleich + +### InviteMemberForm · D-P2 „WF-P2 Admin RBAC" M01 „Mitarbeiter einladen" · A4 Dialog-Formular (kein A-Code im Header, inferiert) +- **Interim-Screen:** `apps/web/app/components/screens/users/InviteMemberForm.tsx` +- **Route(n):** n/a — Sub-Komponente von `MembersSettingsRoute` (rendert im Outlet-Dialog; postet an `routes/app.$tenant/settings/members/new.tsx`) +- **Daten (VM):** keine VM — nutzt `NewMemberActionData` aus `~/routes/app.$tenant/settings/members/new` (via `useActionData`) + `useRootData` (csrf) +- **Benötigte UI-Elemente (KEIN Layout):** `~/ui`: Button, Checkbox, TextInput · Icons: keine +- **Interaktionen (Actions):** keine `…Actions` — `` an `members/new` (Felder `email`, `first-name`, `last-name`, `send-invitation-email`, `csrf`); Prop-Callback `onClose()` +- **States:** Error-Toast (`actionData.error`) · Loading (`navigation.state === "submitting"` → Submit-Button loading) · kein Empty · Permission-Denied via Parent-Route +- **Davids Delta:** ✅ vorhanden (D-P2) — nur Abgleich + +### GroupMembersDialog · D-P2 „WF-P2 Admin RBAC" Gruppen-Bulk (RBAC V2, offener design-nachzug) · A4 Dialog (kein A-Code im Header, inferiert) +- **Interim-Screen:** `apps/web/app/components/screens/users/GroupMembersDialog.tsx` +- **Route(n):** n/a — Sub-Komponente von `GroupsRoute` (`routes/app.$tenant/settings/groups.tsx`, URL `/app/:tenant/settings/groups`; postet Bulk an `settings/groups/:id`) +- **Daten (VM):** `GroupsDetailVM` — `packages/view-models/src/users/groups-detail.vm.ts` (+ Helfer `filterGroupMemberRows`, Typ `GroupMemberRow`) +- **Benötigte UI-Elemente (KEIN Layout):** `~/ui`: Avatar, Button, Checkbox, Dialog, EmptyState, SearchInput · Icons: keine +- **Interaktionen (Actions):** `GroupMembersActions` — onAdd(userIds: string[]), onRemove(userIds: string[]); Prop-Callback `onClose()` +- **States:** Loading (`vm.status !== "ready"` → `null`) · Empty pro Pick-Liste (`EmptyState variant="no-results"`, `emptyMembersKey`/`emptyCandidatesKey`) · Permission-Gate: Add/Remove-Buttons nur bei `data.canManage` +- **Davids Delta:** ✅ vorhanden (D-P2) — nur Abgleich diff --git a/Tickets/Interim-Handover/P03-Audit-Notifications.md b/Tickets/Interim-Handover/P03-Audit-Notifications.md new file mode 100644 index 00000000..a1f058db --- /dev/null +++ b/Tickets/Interim-Handover/P03-Audit-Notifications.md @@ -0,0 +1,74 @@ +# P03 · Audit & Notifications (AK-P3 → D-P3) + +> Interim-UI-Referenz (AD-28). Beide Domänen sind AK-P3-Interim-Screens, rein aus `~/ui` komponiert, VM als +> Prop. **Layout = Davids freie Hand** — Listen nennen nur Elemente/Daten/Aktionen/States. Keine der fünf +> Dateien importiert `@subframe/core`/Icons; alle Affordances aus `~/ui`-Primitives. + +## Audit + +### AuditListPage · Audit-Viewer (A2 + Drawer) +- **Interim-Screen:** `apps/web/app/components/screens/audit/AuditListPage.tsx` +- **Route(n):** `/admin/audit` (`routes/admin/audit.tsx`) — Admin-Host, nicht `app.$tenant`. Permission-Gate `admin.auditTrails.view`. +- **Daten (VM):** `AuditListVM` (Rows/Filter über `AuditListFilterState`, `AuditListRow`) — `packages/view-models/src/audit/audit-list.vm.ts` +- **Benötigte UI-Elemente (KEIN Layout):** Alert · Button · Drawer · EmptyState · Select (+ Select.Item) · Table (HeaderRow/HeaderCell/Row/Cell) · Tag_Label · TextInput (+ TextInput.Input) · DateField (lokal `../tasks/DateField`). Inhaltselemente: Header (Titel + Subtitle + Export-CTA nur bei `canExport`), Filter-Bar (from/to Datum, Actor-Type-Select, Outcome-Select, Action-Text, TargetType-Text, Apply-Button), Tabelle (Zeit/Actor+Kind-Tag/Action monospace/Subjekt/Outcome/Summary + Detail-Button bei `hasDetail`), Server-Pagination (pageInfo + prev/next), Detail-Drawer (Feld-für-Feld-Diff-Tabelle mit „masked"→Tag „Wert geändert", Metadata `
`, leer→Alert). Icons: keine.
+- **Interaktionen (Actions):** `onApply(query: AuditListQuery)` — `AuditListQuery = AuditListFilterState & { page: number }`; getriggert von Filter-Submit, Select-onValueChange, prev/next. Detail-Drawer öffnet rein lokal (`useState openRowId`), kein Action-Callback.
+- **States:** Loading = `vm.status !== "ready"` → leerer Marker-Div · Empty = `rows.length === 0` → EmptyState (`emptyHeadlineKey`/`emptyBodyKey`) · Drawer-Empty = kein Diff/Metadata → Alert · Permission-Denied = Route-Level 404 (`verifyUserHasPermission`), kein In-Component-403.
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte: Quick-Filter (Klick auf Actor/Subject/Action) und „Verlauf dieses Objekts"/„Ähnliche Einträge" aus D-P3-Zeile sind im Interim NICHT gebaut — nur Formular-Filter + Diff-Drawer.
+  - [ ] Neue UI/UX-Entscheidungen: visuelle Unterscheidung der Actor-Kinds (User/Automation/MCP/Agent/Admin) — interim nur neutrales Tag_Label.
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev: Sollen Quick-Filter denselben `onApply` nutzen (Patch je Feld)?
+
+### AuditExportPage · Audit-Export-Jobs (A2)
+- **Interim-Screen:** `apps/web/app/components/screens/audit/AuditExportPage.tsx`
+- **Route(n):** `/admin/audit-export` (`routes/admin/audit-export.tsx`) — Admin-Host. (Tenant-Variante `app.$tenant/settings/audit-export.tsx` = `/app/:tenant/settings/audit-export` rendert eigenes Inline-UI, NICHT diesen Interim-Screen.)
+- **Daten (VM):** `AuditExportVM` (+ `AuditExportResultVM` als `result`-Prop) — `packages/view-models/src/audit/audit-export.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Alert · Button · Select (+ Select.Item, Helper `SelectWithAny`) · TextInput (+ TextInput.Input) · DateField (lokal `../tasks/DateField`). Inhaltselemente: Header (Titel + Beschreibung mit `maxRows`), ein Formular mit drei Submit-Pfaden (GET csv / GET pdf / POST storage-export), Filterfelder (from/to, tenantId optional, regulatoryScope/actorType/outcome je Select-mit-„Any", purpose Pflicht-Textfeld, format Select), Ergebnis-Panel (Erfolg/Warn-Alert mit Download-Link + Integritäts-Warnung, oder Error-Alert). Icons: keine.
+- **Interaktionen (Actions):** KEIN `…Actions`-Typ — reine HTML-Form-Submits gegen die Route (GET streamt CSV/PDF, POST läuft Storage-Export). Einzige Prop neben `vm` ist `result` (Aktions-Ergebnis, null = noch nichts abgeschickt).
+- **States:** Loading = `vm.status !== "ready"` → leerer Marker-Div · Ergebnis: `result.ok===false` → Error-Alert (`errorKey`); `result.ok` → Success/Warning-Alert (Row-Count, Ablauftage, Download-Link, ggf. Integritäts-Warnung); `result===null` → kein Panel · Kein Empty-State · Permission-Denied = Route-Level 404.
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte: D-P3 sieht Export-Job-**Liste** + `/new` + `/:id` (A2) vor — Interim ist ein Single-Formular ohne Job-Liste/Detail-Route.
+  - [ ] Neue UI/UX-Entscheidungen: Zweck-Pflichtfeld (`purpose`) Prominenz; Format-Auswahl CSV/PDF vs. Spec CSV/NDJSON.
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev: Job-Liste/`:id`-Status als eigene D-P3-Screens nachziehen oder Single-Form-Ansatz beibehalten?
+
+## Notifications
+
+### NotificationCenterPage · Notification-Center (A2 + Drawer)
+- **Interim-Screen:** `apps/web/app/components/screens/notifications/NotificationCenterPage.tsx`
+- **Route(n):** `/app/:tenant/notifications` (`routes/app.$tenant/notifications.tsx`) — Route hostet SSE-Subscription (`useNotificationsStream`).
+- **Daten (VM):** `NotificationCenterVM` (Items über `NotificationCenterItemVM`) — `packages/view-models/src/notifications/notification-center.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Alert · BadgeFeedback · Button · EmptyState · Tabs (+ Tabs.Item). Inhaltselemente: Header (Titel + Diagnostics-Subtitle + sr-only Live-Region für `connectionStatusKey`), Tabs Alle/Ungelesen mit Counts, Bulk-Clear-Button (nur bei `unreadCount>0`), Notification-Cards (Message als Deeplink-Link oder Text, Kategorie·Channel·Zeit-Metazeile, Status-Badge farbcodiert READ/FAILED/PENDING, Bounce-Alert bei `bounceKey` mit `lastErrorRedacted`, Mark-Read-Button bei ungelesen). Icons: keine.
+- **Interaktionen (Actions):** `onMarkRead(deliveryId: string)` · `onMarkAllRead()`. Tab-Umschaltung Alle/Ungelesen ist lokal (`useState`).
+- **States:** Loading = `vm.status !== "ready"` → leerer Marker-Div · Empty = `totalCount===0` → EmptyState (`emptyHeadlineKey`/`emptyBodyKey`) · Unread-Empty = gefilterte Liste leer → EmptyState (`unreadEmptyKey`) · Permission-Denied = Route-Level (Auth-Middleware), kein In-Component-403.
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte: D-P3 nennt Prioritäts-Marker + Mark-**unread** — Interim hat nur Mark-read (kein Priority-Marker, kein Unread-Zurücksetzen).
+  - [ ] Neue UI/UX-Entscheidungen: Bounce-Klartext-Darstellung („Emails an dich kommen aktuell nicht an") als Error-Alert je Card.
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev: Priority-Marker-Datenfeld im VM ergänzen?
+
+### NotificationsDrawer · Notification-Center Topbar-Drawer (A2 + Drawer)
+- **Interim-Screen:** `apps/web/app/components/screens/notifications/NotificationsDrawer.tsx`
+- **Route(n):** n/a — Shell-Level-Komponente (globaler Drawer, gehostet von `apps/web/app/components/layouts/NavBar.tsx`; NavBar besitzt SSE-Subscription + Open-State).
+- **Daten (VM):** `NotificationCenterItemVM` (kein eigener VM; Items als Prop, max 10) — `packages/view-models/src/notifications/notification-center.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback · Button · Drawer (+ Drawer.Content) · EmptyState. Inhaltselemente: Header (Titel + Close-Button), Item-Liste (max 10; Message als Deeplink oder Text, ungelesen fett, Zeitstempel, „Ungelesen"-Badge bei nicht gelesen), „Alle anzeigen"-Link (`allHref`). Icons: keine.
+- **Interaktionen (Actions):** KEIN `…Actions`-Typ — Props `open`, `items`, `allHref`, `onClose()`. Deeplink-Klicks folgen validierten `deeplinkHref`; „Alle anzeigen" navigiert zu `allHref`.
+- **States:** Empty = `visibleItems.length===0` → EmptyState (`drawer.empty` + `emptyHelper`) · kein eigenes Loading/Permission-Denied (Shell steuert Sichtbarkeit).
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte: kein Mark-read/Bulk-Clear im Drawer (nur „Alle anzeigen" → Center).
+  - [ ] Neue UI/UX-Entscheidungen: Verhältnis Bell-Slot (D-D0a) ↔ Drawer-Öffnung; 10-Item-Cap.
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev: Soll der Drawer Inline-Mark-read bekommen oder bewusst read-only bleiben?
+
+### NotificationSettingsPage · Notification-Settings (A6)
+- **Interim-Screen:** `apps/web/app/components/screens/notifications/NotificationSettingsPage.tsx`
+- **Route(n):** `/app/:tenant/settings/notifications` (`routes/app.$tenant/settings/notifications.tsx`) — Actions fetcher-backed.
+- **Daten (VM):** `NotificationSettingsVM` (Channel-Typ `NotificationSettingChannel`) — `packages/view-models/src/notifications/notification-settings.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Alert · Switch (+ lokaler `ChannelToggle`-Wrapper). Inhaltselemente: Header (Heading + Description), Save-State-Alert, Master-Karte „alle Emails aus" (Switch), pro Modul-Gruppe eine Karte mit Modul-Label + je Kategorie In-App/Email-Toggle. Icons: keine.
+- **Interaktionen (Actions):** `onTogglePreference({ category, channel, enabled })` · `onSetEmailMaster(disableAllEmails: boolean)` (Master-Switch schreibt alle Kategorien). Zusätzliche Prop `saveState: { ok; errorText? } | null`.
+- **States:** Loading = `vm.status !== "ready"` → leerer Marker-Div · Save = `saveState.ok` → Success-Alert / sonst Error-Alert (`errorText`) · `saveState===null` → nichts · kein Empty-State (Registry liefert immer Gruppen) · Permission-Denied = Route-Level.
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte: D-P3 nennt Priority-Selektor je Event-Typ — Interim hat nur In-App/Email-Toggles, keinen Priority-Selektor.
+  - [ ] Neue UI/UX-Entscheidungen: Master-Toggle-Klartext „betrifft auch zukünftige Event-Typen".
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev: Priority pro Kategorie im VM/Registry ergänzen?
diff --git a/Tickets/Interim-Handover/P04-Files-Tasks.md b/Tickets/Interim-Handover/P04-Files-Tasks.md
new file mode 100644
index 00000000..98f7bb4a
--- /dev/null
+++ b/Tickets/Interim-Handover/P04-Files-Tasks.md
@@ -0,0 +1,87 @@
+# P04 · Files & Tasks (AK-P4a/b → D-P4)
+
+> Interim-UI-Referenz (AD-28) für zwei Domänen (Dev-DoD 100 %, `🎨 design-nachzug: offen`). Alle Screens aus
+> `~/ui` komponiert, VM als Prop, Backend/Loader/Action stabil. **Layout = Davids freie Hand.** Routen liegen
+> unter `apps/web/app/routes/app.$tenant/`, laufen real unter `/:tenant/` (Kurzform wie in D-P4).
+
+## Files
+
+### FilesBrowserPage · Files-Browser [`/files`] (A2)
+- **Interim-Screen:** `apps/web/app/components/screens/files/FilesBrowserPage.tsx`
+- **Route(n):** `/files` (`routes/app.$tenant/files._index.tsx`)
+- **Daten (VM):** `FileBrowserVM` — `packages/view-models/src/files/file-browser.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** ToggleGroup (Table/Grid-Umschalter), Button, Select (MIME/Entität/Tag/Verantwortlicher-Filter), Checkbox (Archiv-Toggle + Row-/Select-All), Table (+ HeaderRow/HeaderCell/Row/Cell), Card (Grid-Kacheln mit Thumbnail), BadgeFeedback (Preview-/Scan-Status), Tag_Label (Tags), plus native ``-Bulk-ZIP-Leiste · Icons: FeatherList, FeatherGrid, FeatherUpload, FeatherDownload, FeatherFile
+- **Interaktionen (Actions):** `onApply(query: FilesBrowserQuery)` (server-side Navigation: mimeGroup/entityType/tag/assignedToUserId/includeArchived/viewMode), `onToggleSelect(id)`, `onToggleSelectAll(ids, selectAll)`; zusätzlich Upload-CTA-Link (`uploadHref`), Cursor-Pagination-Link (`nextHref`), Bulk-ZIP-POST (`bulkZipAction`, `selectedIds`), Row-Detail-Links
+- **States:** Empty (`emptyHeadlineKey`/`emptyBodyKey` + optional Upload-CTA) · Loading/not-ready (leerer Marker `ui.files.browser`) · Permission-Denied = 404 (Loader/Guard, kein UI-State); Upload-CTA nur bei `canWrite`
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### FileUploadPage · Upload-View + Datei-Dropzone [`/files/upload`] (A4)
+- **Interim-Screen:** `apps/web/app/components/screens/files/FileUploadPage.tsx`
+- **Route(n):** `/files/upload` (`routes/app.$tenant/files.upload.tsx`; Resource-Routes: `files.upload.init.tsx`, `files.upload.$uploadId.chunk.$index.tsx`, `files.upload.$uploadId.complete.tsx`)
+- **Daten (VM):** `FileUploadVM` — `packages/view-models/src/files/file-upload.vm.ts` (liefert `dropzoneHeadlineKey`/`dropzoneBodyKey`/`heicNoticeKey`); Queue-State separat als `items: FileUploadDisplayItem[]` (Route-injiziert)
+- **Benötigte UI-Elemente (KEIN Layout):** Custom Drag&Drop-Dropzone (kein i18n-fähiges `~/ui`-Dropzone vorhanden — bewusst handgebaut), Button, Progress (Per-Datei-Fortschritt, State uploading/success/error), Alert (HEIC→JPG-Hinweis), Queue-Liste mit Per-Datei-Status · Icons: FeatherUpload
+- **Interaktionen (Actions):** `onFiles(files: FileList | null)` (via Klick-Input + Drop); Client-Chunked-Pipeline (init/chunk/complete) lebt in der Route
+- **States:** Empty (`files.upload.queue.empty`) · Per-Item queued/uploading/processing/done/error (mit `errorKey`, Retry-Text) · Loading/not-ready (leerer Marker `ui.files.upload`) · Permission-Denied = 404 (`files.write`-Gate im Loader)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### FileDetailPage · File-Detail [`/files/:id`] (A3)
+- **Interim-Screen:** `apps/web/app/components/screens/files/FileDetailPage.tsx`
+- **Route(n):** `/files/:id` (`routes/app.$tenant/files.$id.tsx`); Medien via `media.$fileId.$variant.tsx` (Auth-Proxy)
+- **Daten (VM):** `FileDetailData` (entpackt aus `FileDetailVM`) — `packages/view-models/src/files/file-detail.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Button, IconButton, Card (Stammdaten/Metadaten-Grid), Alert (Scan-Notice fail-closed + Preview-Fallback-Klartext), Tag_Label (Tags) + TextInput-Tag-Editor, Select (Entity-Type im Add-Link), TextInput (entityId/linkRole), Dialog (Add-Link S 560 + Delete XS 420), Bild-/PDF-Preview-Container · Icons: FeatherDownload, FeatherRefreshCw, FeatherArchive, FeatherTrash2, FeatherLink, FeatherX
+- **Interaktionen (Actions):** Server-``+`intent` — `regeneratePreview`, `archive`, `delete`, `updateMeta` (Tags), `link` (entityType/entityId/linkRole), `unlink` (entityType/entityId); Download-Link (`downloadUrl`); lokaler State nur für Dialog-Öffnen (addLinkOpen/deleteOpen)
+- **States:** Scan-Notice (infected=error / pending=warning) · Preview vorhanden / `previewFallbackKey`-Klartext + Download · Linked-Entities Empty (`files.detail.links.empty`) · Tags-Editor/Aktionen nur bei `canWrite`/`canDelete`/`canRegeneratePreview` · Permission-Denied = 404 (Guard)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+## Tasks
+
+### TasksListPage · Task-Liste [`/tasks`] (A2 — wiederverwendbare Komponente)
+- **Interim-Screen:** `apps/web/app/components/screens/tasks/TasksListPage.tsx`
+- **Route(n):** `/tasks` (`routes/app.$tenant/tasks._index.tsx`) — Host der embeddable Liste; Entity-Detail-Views betten sie ein (R-P6 ff.)
+- **Daten (VM):** `TaskListVM` — `packages/view-models/src/tasks/task-list.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** ToggleGroup (Presets Meine/Erstellt/Alle), Select (Status- + Prioritäts-Filter), Checkbox (Archiv- + Demo-Daten-Toggle), Button (Neu-CTA + Pagination), Table (Titel/Status/Priorität/Assignee/Deadline), BadgeFeedback (Status), Tag_Label (Priorität), EmptyState · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onApply(query: TaskListQuery)` (server-side Navigation: preset/status/priority/includeArchived/includeDemoData); Neu-CTA-Link (`createHref`), Cursor-Pagination-Link (`nextHref`), Row-Detail-Links; `data-overdue`-Marker auf Deadline
+- **States:** Empty (`emptyHeadlineKey`/`emptyBodyKey` via `EmptyState`) · Loading/not-ready (leerer Marker `ui.tasks.list`) · Neu-CTA nur bei `canWrite` · Permission-Denied = 404 (Preset „Alle" nur mit `tasks.read_all`)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### TaskDetailPage · Task-Detail [`/tasks/:id`] (A3 + A5)
+- **Interim-Screen:** `apps/web/app/components/screens/tasks/TaskDetailPage.tsx`
+- **Route(n):** `/tasks/:id` (`routes/app.$tenant/tasks.$id.tsx`)
+- **Daten (VM):** `TaskDetailData` (entpackt aus `TaskDetailVM`) — `packages/view-models/src/tasks/task-detail.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, BadgeFeedback (Status) + Tag_Label (Priorität) + Quelle-Marker, Card (Overview-Metadaten + Edit-Formular), TextInput/TextArea (Titel/Beschreibung), Select (Status/Priorität/Assignee-Typ/Assignee/Watcher), DateField (native Date-Input-Helfer, kein `~/ui`-Äquivalent), Button/IconButton, Watcher-Liste, Dialog (Delete XS 420) · Icons: FeatherCheck, FeatherArchive, FeatherTrash2
+- **Interaktionen (Actions):** Server-``+`intent` — `complete`, `archive`, `delete`, `update` (title/description/status/priority/deadline), `reassign` (assigneeType user↔group + assigneeId), `addWatcher` (userId), `removeWatcher` (userId); lokaler State nur für Delete-Dialog
+- **States:** Overdue-Marker (`data-overdue`, `isOverdue`) · linkedEntity optional · Watchers Empty (`tasks.detail.watchers.empty`) · Edit-/Reassign-/Watcher-/Complete-/Archive-Blöcke nur bei `canWrite`, Delete nur bei `canDelete`; „Erledigen" ausgeblendet wenn Status=done · Unassigned-Fallback-Text · Permission-Denied = 404 (fremde Task)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### TaskNewPage · Task-Anlage [`/tasks/new`] (A5 — Anlage-Modal/Form)
+- **Interim-Screen:** `apps/web/app/components/screens/tasks/TaskNewPage.tsx`
+- **Route(n):** `/tasks/new` (`routes/app.$tenant/tasks.new.tsx`)
+- **Daten (VM):** kein VM-Wrapper — Loader-injizierte Props `assignableUsers`/`assignableGroups`/`prefill`/`cancelHref` (`TaskPickerOption`, `TaskNewPrefill`); Typ `TaskPriority` aus `@hivegrid/view-models` (`packages/view-models/src/tasks/task-list.vm.ts`)
+- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Card, TextInput (Titel), TextArea (Beschreibung), Select (Priorität/Assignee-Typ user↔group/Assignee mit Creator-Default-Sentinel `__creator__`), DateField (Deadline), Button (Anlegen + Cancel-Link), optionaler Prefill-Marker · Icons: FeatherPlus
+- **Interaktionen (Actions):** Server-`` → Route-Action `createTask` (title/description/priority/deadline/assigneeType/assigneeId; optional hidden entityType/entityId aus `prefill`); Cancel-Link (`cancelHref`); Assignee-Default `__creator__` mappt server-seitig auf „kein expliziter Assignee"
+- **States:** Prefill-Hinweiszeile nur wenn `prefill` gesetzt (Anlage aus Entity-Detail-CTA); Permission-Denied = 404 (`tasks.write`-Gate); reines Form, kein Empty/Loading
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
diff --git a/Tickets/Interim-Handover/P05-Integrations.md b/Tickets/Interim-Handover/P05-Integrations.md
new file mode 100644
index 00000000..6e7a6cfa
--- /dev/null
+++ b/Tickets/Interim-Handover/P05-Integrations.md
@@ -0,0 +1,82 @@
+# P05 · Integrations / Apps-Engine (R-P5 → D-P5)
+
+> Interim-UI-Referenz (AD-28). Lauffähige `~/ui`-Vorschau-Screens (VM als Prop) im gebundelten
+> `03 Quellen und Referenzen/hivegrid-cloud/`-Checkout. **Layout = Davids freie Hand.**
+
+### IntegrationsCatalogPage · D-P5 „Integrations-Katalog" (Archetyp A6)
+- **Interim-Screen:** `apps/web/app/components/screens/integrations/IntegrationsCatalogPage.tsx`
+- **Route(n):** `/:tenant/integrations` (`routes/app.$tenant/integrations._index.tsx`)
+- **Daten (VM):** `IntegrationCatalogVM` — `packages/view-models/src/apps-engine/integration-catalog.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Card, BadgeFeedback (Verbindungs-Zähler / Status-Licht), SearchInput (client-seitiger Filter), EmptyState (inkl. `variant="no-results"`), Link · Icons: keine
+- **Interaktionen (Actions):** keine Mutationen (reines Navigations-Screen); nur client-State `query` (Suchfilter) + `Link` auf `row.detailHref`. Karten nach Kategorie gruppiert (Reihenfolge = erstes Auftreten).
+- **States:** Empty (`emptyHeadlineKey`/`emptyBodyKey` bei 0 Apps) · No-results (Filter ohne Treffer) · Loading (VM `status !== "ready"` → leerer Container) · Permission-Denied: Route wirft 404 (`integrations.read` fehlt), Modul aus → locked empty
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### ConnectionDetailPage · D-P5 „App-Detail / Connection-Verwaltung" (Archetyp A6+A2)
+- **Interim-Screen:** `apps/web/app/components/screens/integrations/ConnectionDetailPage.tsx`
+- **Route(n):** `/:tenant/integrations/:app` (`routes/app.$tenant/integrations.$app._index.tsx`)
+- **Daten (VM):** `ConnectionDetailVM` (+ `ConnectionRow`) — `packages/view-models/src/apps-engine/connection-detail.vm.ts`; zusätzlich `AppCredentialField[]` aus `@hivegrid/integrations` als Prop
+- **Benötigte UI-Elemente (KEIN Layout):** Alert (Screen-Banner bei degraded/broken), Button, Dialog (Disconnect-Confirm, XS 420), EmptyState, LinkButton (Breadcrumb zurück), plus Sub-Komponenten `ConnectionCard` (Liste, 2-spaltiges Grid) und `ConnectWizardDialog` · Icons: FeatherPlus
+- **Interaktionen (Actions `ConnectionDetailActions`):** `onConnect(input: ConnectWizardSubmit)` · `onReconnect(connectionId, input: ConnectWizardSubmit)` · `onDisconnect(connectionId)` · `onManualSync(connectionId)`; lokaler Dialog-State (`wizard`, `disconnectRow`), `closeSignal` schließt Dialoge nach Erfolg
+- **States:** Empty (`emptyHeadlineKey`/`emptyBodyKey` + Connect-CTA) · Loading (leerer Container) · Banner-State (mind. 1 degraded/broken → `error`/`warning` + Reconnect-Button) · Permission-Denied: Route 404 (`notFoundResponse` mehrfach)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### ConnectionCard · D-P5 App-Card mit Status-Ampel (Sub-Komponente, kein eigener Archetyp)
+- **Interim-Screen:** `apps/web/app/components/screens/integrations/ConnectionCard.tsx`
+- **Route(n):** n/a — Sub-Komponente von `ConnectionDetailPage` (wird von P14/P15/P16/P20-App-Connections wiederverwendet)
+- **Daten (VM):** `ConnectionRow` — `packages/view-models/src/apps-engine/connection-detail.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Card, BadgeFeedback (Status-Ampel via `connectionStatusBadge`), Button (secondary/destructive), LinkButton, Link (→ `row.syncsHref`) · Icons: keine. Zeigt Label, Klartext-`healthMessageKey`, letzter Health-Check (Intl-DateTime)
+- **Interaktionen (Actions):** `onManualSync(connectionId)` · `onReconnect(row)` · `onDisconnect(row)`; Buttons gated über `canManualSync`/`canReconnect`/`canDisconnect` + Status (`revoked`/`degraded`/`broken`)
+- **States:** kein eigenes Empty/Loading (Parent steuert); Button-`submitting`-Loading; `data-status` steuert Sichtbarkeit der Lifecycle-Buttons
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### ConnectWizardDialog · D-P5 „Connection-Setup-Wizard" (Archetyp A5+A6, Größe M 720)
+- **Interim-Screen:** `apps/web/app/components/screens/integrations/ConnectWizardDialog.tsx`
+- **Route(n):** n/a — Sub-Komponente von `ConnectionDetailPage`
+- **Daten (VM):** kein `@hivegrid/view-models`-Typ; Props aus `AppCredentialField[]` (`@hivegrid/integrations`) + lokale Typen `ConnectWizardMode`, `ConnectWizardSubmit`
+- **Benötigte UI-Elemente (KEIN Layout):** Dialog (720px), StepperHorizontal (+ `.Step`: prerequisites → credentials → confirm), TextInput (+ `.Input`, Label + Secret-Felder), IconButton (Reveal-Toggle), Alert (Error / OAuth-Hinweis / no-fields), Button (Cancel/Back/Next/Connect) · Icons: FeatherEye, FeatherEyeOff
+- **Interaktionen (Actions):** `onSubmit(input: {label, credentials})` (postet connect/reconnect-Route-Action) · `onClose()`; lokal: Schritt-Navigation, `toggleReveal(name)`, Validierung (`labelMissing`/`requiredMissing`), OAuth2-ohne-Felder-Sonderfall
+- **States:** Error (`errorMessage` → Alert) · OAuth2-ohne-Formular (Info-Alert, geführter Redirect erst P14/P16) · Submitting (Connect-Button-Loading) · Disabled-Next bei unvollständigen Credentials
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### SyncHistoryPage · D-P5 „Sync-History" (Archetyp A2)
+- **Interim-Screen:** `apps/web/app/components/screens/integrations/SyncHistoryPage.tsx`
+- **Route(n):** `/:tenant/integrations/:app/:connectionId/syncs` (`routes/app.$tenant/integrations.$app.$connectionId.syncs._index.tsx`)
+- **Daten (VM):** `SyncHistoryVM` (+ `SyncKind`, `SyncStatus`) — `packages/view-models/src/apps-engine/sync-history.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Table (+ `.HeaderRow`/`.HeaderCell`/`.Row`/`.Cell`), Select (Status- + Kind-Filter, client-seitig), BadgeFeedback (`syncStatusBadge`), Button (Re-Sync), EmptyState (+ `no-results`), LinkButton, Link (→ Run-Detail `${runHrefBase}/${row.id}`) · Icons: keine. Spalten: started/kind/status/items (succeeded/total, failed ✗)/summary
+- **Interaktionen (Actions `SyncHistoryActions`):** `onManualSync(connectionId)` (gated `canManualSync`); lokaler Filter-State `status`/`kind`
+- **States:** Empty (`emptyHeadlineKey`/`emptyBodyKey`) · No-results (Filter leer) · Loading (leerer Container) · Permission-Denied: Route 404
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### SyncRunDetailPage · D-P5 „Sync-Run-Detail" (Archetyp A3)
+- **Interim-Screen:** `apps/web/app/components/screens/integrations/SyncRunDetailPage.tsx`
+- **Route(n):** `/:tenant/integrations/:app/:connectionId/syncs/:runId` (`routes/app.$tenant/integrations.$app.$connectionId.syncs.$runId.tsx`)
+- **Daten (VM):** `SyncRunDetailVM` — `packages/view-models/src/apps-engine/sync-run-detail.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Card (Counter total/succeeded/failed + Klartext-Summary), Alert (Error bei `status==="failed"`), Table (AppSyncItem: entity/externalId/operation/status/error), BadgeFeedback (`syncStatusBadge`/`syncItemStatusBadge`/`syncLogSeverityBadge`), EmptyState (leere Items), LinkButton, Link (Breadcrumb); Log-Zeilen mit Severity-Badge + Timestamp · Icons: keine
+- **Interaktionen (Actions):** keine (reines Read-Only-Detail); nur `Link` zurück (`backHref`)
+- **States:** Empty-Items (`emptyItemsHeadlineKey`/`emptyItemsBodyKey`) · Failed-Run (Error-Alert) · Loading (leerer Container) · Permission-Denied: Route 404
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
diff --git a/Tickets/Interim-Handover/P06-CRM.md b/Tickets/Interim-Handover/P06-CRM.md
new file mode 100644
index 00000000..79aad4f1
--- /dev/null
+++ b/Tickets/Interim-Handover/P06-CRM.md
@@ -0,0 +1,83 @@
+# P06 · CRM (R-P6 → D-CRM)
+
+> Interim-UI-Referenz (AD-28). Jeder Screen unten ist als lauffähiger `Page`-Vorschau-Screen aus `~/ui`
+> gebaut (VM als Prop) und liegt im gebundelten Checkout unter `03 Quellen und Referenzen/hivegrid-cloud/`.
+> **Layout = Davids freie Hand** — die Listen unten nennen nur benötigte Elemente/Daten/Aktionen, keine Anordnung.
+
+### KontaktListPage · D-CRM "Kontakt-Liste"/"Organisationen-Liste", archetype A2
+- **Interim-Screen:** `apps/web/app/components/screens/crm/KontaktListPage.tsx`
+- **Route(n):** `/app/:tenant/contacts` (`routes/app.$tenant/contacts._index.tsx`) · `/app/:tenant/contacts/companies` (`routes/app.$tenant/contacts.companies._index.tsx`) — ein Screen, prop `variant: "contacts" | "companies"`
+- **Daten (VM):** `KontaktListVM` — `packages/view-models/src/crm/kontakt-list.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Checkbox, EmptyState, Select, Table, TextInput · Icons: FeatherDownload, FeatherPlus
+- **Interaktionen (Actions):** `onApplyFilter(query: KontaktListQuery)`, `onGoToPage(page: number)`, `onBulk(intent: "bulkArchive"|"bulkReassign"|"bulkTag", ids: string[], extra?: {zustaendigerUserId?, tagId?})`, `onNew()`, `onExport()`
+- **States:** Empty (EmptyState, leere Liste) · Permission-Denied/Modul-gesperrt (EmptyState `crm.locked.*`; Route 404 wenn nicht sichtbar) · Error (BadgeFeedback `subtle-negative`). CTA/Bulk/Admin-Nav gated über `canWrite`/`canAdmin`
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### KontaktDetailPage · D-CRM "Kontakt-Detail"/"Organisation-Detail", archetype A3 (master template)
+- **Interim-Screen:** `apps/web/app/components/screens/crm/KontaktDetailPage.tsx`
+- **Route(n):** `/app/:tenant/contacts/:id` (`routes/app.$tenant/contacts.$id.tsx`) · `/app/:tenant/contacts/companies/:id` (`routes/app.$tenant/contacts.companies.$id.tsx`)
+- **Daten (VM):** `KontaktDetailData` (aus `KontaktDetailVM`) + `ActivityStreamVM` — `packages/view-models/src/crm/kontakt-detail.vm.ts` (+ `packages/view-models/src/crm/activity-stream.vm.ts`)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Breadcrumbs, Button, Card, Checkbox, EmptyState, Select, TextArea, TextInput · Icons: keine
+- **Interaktionen (Actions):** ein Callback `onSubmit(intent: string, fields?: Record)`; Intents: `updateBasics`, `addEmail`/`removeEmail`/`setPrimaryEmail`, `addTelefon`/`removeTelefon`/`setPrimaryTelefon`, `addRelation`/`removeRelation`, `addTag`/`removeTag`, `setCustomField`, `reassign`, `addNote`, `archive`/`reactivate`, `delete`
+- **States:** Empty (Activity-Stream: EmptyState `crm.detail.activityEmpty`) · Permission-Denied(404) auf Route-Ebene · Error (BadgeFeedback). Alle Edit-Blöcke gated über `data.canWrite`/`data.canDelete`
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### KontaktNewPage · D-CRM "Kontakt anlegen", archetype A4
+- **Interim-Screen:** `apps/web/app/components/screens/crm/KontaktNewPage.tsx`
+- **Route(n):** `/app/:tenant/contacts/new` (`routes/app.$tenant/contacts.new.tsx`)
+- **Daten (VM):** kein `@hivegrid/view-models`-Import — Formular hält lokalen Draft `KontaktNewFields`; Live-Duplikate über prop `duplicates: KontaktNewDuplicates` (route-geliefert, `{hard, soft}` von `KontaktNewDuplicateCard`)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Breadcrumbs, Button, Card, Select, TextInput · Icons: keine
+- **Interaktionen (Actions):** `onCheckDuplicates(fields: KontaktNewFields)` (live, onBlur der Felder), `onCreate(fields: KontaktNewFields)`
+- **States:** Permission-Denied/Modul-gesperrt (BadgeFeedback `crm.locked.headline`) · Error (BadgeFeedback) · Duplikat-Warnung (Card `negative` bei hard / `warning` bei soft)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### CustomFieldAdminPage · D-CRM "Custom-Felder", archetype A2
+- **Interim-Screen:** `apps/web/app/components/screens/crm/CustomFieldAdminPage.tsx`
+- **Route(n):** `/app/:tenant/contacts/admin/custom-fields` (`routes/app.$tenant/contacts.admin.custom-fields.tsx`)
+- **Daten (VM):** `CustomFieldAdminVM` — `packages/view-models/src/crm/custom-field-admin.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, EmptyState, Select, Table, TextInput · Icons: keine
+- **Interaktionen (Actions):** `onCreate(input: {key, label, datentyp, optionen})`, `onArchive(definitionId: string)`
+- **States:** Empty (EmptyState `crm.admin.customFields.empty`) · Permission-Denied/Modul-gesperrt (BadgeFeedback `crm.locked.headline`) · Error (BadgeFeedback)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### TagAdminPage · D-CRM "Tag-Verwaltung", archetype A2
+- **Interim-Screen:** `apps/web/app/components/screens/crm/TagAdminPage.tsx`
+- **Route(n):** `/app/:tenant/contacts/admin/tags` (`routes/app.$tenant/contacts.admin.tags.tsx`)
+- **Daten (VM):** `TagAdminVM` (Zeilen: `TagAdminRowData`) — `packages/view-models/src/crm/tag-admin.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, EmptyState, TextInput (+ native ``, kein ~/ui-Äquivalent) · Icons: keine
+- **Interaktionen (Actions):** `onCreate({name, farbe})`, `onUpdate({tagId, name, farbe})`, `onDelete(tagId: string)`
+- **States:** Empty (EmptyState `crm.admin.tags.empty`) · Permission-Denied/Modul-gesperrt (BadgeFeedback `crm.locked.headline`) · Error (BadgeFeedback)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### DuplicateToolPage · D-CRM "Duplikat-Tool", archetype A5
+- **Interim-Screen:** `apps/web/app/components/screens/crm/DuplicateToolPage.tsx`
+- **Route(n):** `/app/:tenant/contacts/admin/duplicates` (`routes/app.$tenant/contacts.admin.duplicates.tsx`)
+- **Daten (VM):** `DuplicateVM` (Paare: `pair.a`/`pair.b`, `matchType` hard/soft) — `packages/view-models/src/crm/duplicate.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Card, EmptyState · Icons: keine
+- **Interaktionen (Actions):** keine (read-only; Merge folgt später, VM `canMerge=false`). Prop `detailHrefFor(kontaktId)` liefert Detail-Links
+- **States:** Empty (EmptyState `crm.admin.duplicates.empty`) · Permission-Denied/Modul-gesperrt (BadgeFeedback `crm.locked.headline`)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
diff --git a/Tickets/Interim-Handover/P07-Leads.md b/Tickets/Interim-Handover/P07-Leads.md
new file mode 100644
index 00000000..ea1a92fa
--- /dev/null
+++ b/Tickets/Interim-Handover/P07-Leads.md
@@ -0,0 +1,83 @@
+# P07 · Leads (R-P7 → D-P7)
+
+> Interim-UI-Referenz (AD-28). Lauffähige `Page`-Vorschau-Screens aus `~/ui` (VM als Prop) im gebundelten
+> `03 Quellen und Referenzen/hivegrid-cloud/`-Checkout. **Layout = Davids freie Hand** — Listen nennen nur
+> Elemente/Daten/Aktionen.
+
+### LeadInboxPage · D-P7 „Lead-Inbox" (Archetyp A2 + Triage)
+- **Interim-Screen:** `apps/web/app/components/screens/leads/LeadInboxPage.tsx`
+- **Route(n):** `/:tenant/leads` (`routes/app.$tenant/leads._index.tsx`)
+- **Daten (VM):** `LeadListVM` — `packages/view-models/src/leads/lead-list.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Checkbox, EmptyState, Select, Table, TextInput · Icons: FeatherBarChart2, FeatherPlus
+- **Interaktionen (Actions):** onApplyFilter(query: LeadListQuery), onSetStatus(leadId, status), onBulkStatus(leadIds[], status), onNextPage(), onNew(), onStatistics(); zusätzliche Datenpunkte: Filter (Suche/Quelle/Spam ausblenden/Converted zeigen), Bulk-Status-Select, Zeilen-Marker (duplicate/spam/converted), Per-Zeile Qualify/Spam, „nächste Seite", SSE-Realtime-Hint (`sseHref`), Admin-Quicklinks (sourcesHref/antiSpamHref)
+- **States:** Empty (EmptyState — leere Liste `emptyHeadlineKey`/`emptyBodyKey`; Modul-off = `leads.list.locked`) / Loading (synchron im Loader, kein Spinner; `submitting`-Flag an Bulk-Button) / Permission-Denied (404 auf Route bei fehlendem `leads.read_own`)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### LeadDetailPage · D-P7 „Lead-Detail" (Archetyp A3) + „Lead → Projekt" (A5)
+- **Interim-Screen:** `apps/web/app/components/screens/leads/LeadDetailPage.tsx`
+- **Route(n):** `/:tenant/leads/:id` (`routes/app.$tenant/leads.$id.tsx`)
+- **Daten (VM):** `LeadDetailVM` — `packages/view-models/src/leads/lead-detail.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Breadcrumbs, Button, Card, Select, TextArea, TextInput · Icons: keine
+- **Interaktionen (Actions):** onSubmit(intent, fields?) — Intents: `update` (Stammdaten vorname/nachname/email/telefon/message/freigabe), `setStatus` (status), `convert` (kontaktId → Lead→Projekt, kein Modal-Zwang); Anzeige: Status-/Marker-Badges, Spam-Score+Reason, DSGVO-Auto-Delete-Hinweis, „konvertiert"-Badge
+- **States:** Empty (kein Data → leerer Container-Shell) / Loading (synchron; `submitting` an Save/Convert) / Permission-Denied (404 auf Route; Schreibfelder via `canWrite` disabled)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen: (Pipeline-/Start-Status-Auswahl beim Convert ist getrackter design-nachzug — Backend nimmt nur projektId/kontaktId)
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### LeadNewPage · D-P7 „Manueller Lead-Eintrag" (Archetyp A4)
+- **Interim-Screen:** `apps/web/app/components/screens/leads/LeadNewPage.tsx`
+- **Route(n):** `/:tenant/leads/new` (`routes/app.$tenant/leads.new.tsx`)
+- **Daten (VM):** kein `@hivegrid/view-models`-VM — lokaler Formular-Draft `LeadNewFields` (quelle, vorname, nachname, email, telefon, message)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Breadcrumbs, Button, Card, Select, TextArea, TextInput · Icons: keine
+- **Interaktionen (Actions):** onCreate(fields: LeadNewFields); Submit disabled bis mind. ein Identitätsfeld gefüllt (`hasIdentity`)
+- **States:** Empty (n/a — reines Formular) / Loading (`submitting` an Submit-Button) / Permission-Denied (404 auf Route); Fehler via `errorKey`-Badge
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### LeadStatisticsPage · D-P7 „Lead-Statistiken" (Archetyp A2 + KPI-Kit)
+- **Interim-Screen:** `apps/web/app/components/screens/leads/LeadStatisticsPage.tsx`
+- **Route(n):** `/:tenant/leads/statistics` (`routes/app.$tenant/leads.statistics.tsx`)
+- **Daten (VM):** `LeadStatisticsVM` — `packages/view-models/src/leads/lead-statistics.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Card, StatsCard · Icons: FeatherInbox
+- **Interaktionen (Actions):** keine Callbacks (read-only); Navigation zurück via `listHref`; Datenpunkte: KPI-Kacheln (`kpis`), Aufschlüsselung nach Quelle (`byQuelle`) und Status (`byStatus`)
+- **States:** Empty (kein Data → leerer Container-Shell; keine dedizierte EmptyState-Komponente) / Loading (synchron im Loader) / Permission-Denied (404 auf Route)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen: (reichere Breakdowns — Reaktionszeit, per-Owner, Wochentag, Trend-Charts — noch nicht vom Service geliefert, getrackter design-nachzug)
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### LeadSourcesAdminPage · D-P7 „Quellen-Verwaltung" (Archetyp A2/A6)
+- **Interim-Screen:** `apps/web/app/components/screens/leads/LeadSourcesAdminPage.tsx`
+- **Route(n):** `/:tenant/admin/lead-sources` (`routes/app.$tenant/admin.lead-sources.tsx`)
+- **Daten (VM):** `LeadSourceAdminVM` — `packages/view-models/src/leads/lead-source-admin.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, EmptyState, Table, TextInput · Icons: keine
+- **Interaktionen (Actions):** onUpsert(input: {key, name, displayLabel}); Tabelle mit Typ-Badge, Paused-/Webhook-Status-Badges (Token nur als „vorhanden ja/nein", da gehasht); Create-Card via `canWrite`
+- **States:** Empty (EmptyState `leads.sources.empty`) / Loading (`submitting` an Save-Button) / Permission-Denied (404 auf Route; Create-Card nur bei `canWrite`)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### LeadAntiSpamAdminPage · D-P7 „Anti-Spam-Settings" (Archetyp A6)
+- **Interim-Screen:** `apps/web/app/components/screens/leads/LeadAntiSpamAdminPage.tsx`
+- **Route(n):** `/:tenant/admin/lead-anti-spam` (`routes/app.$tenant/admin.lead-anti-spam.tsx`)
+- **Daten (VM):** `LeadAntiSpamVM` — `packages/view-models/src/leads/lead-anti-spam.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Select, TextInput · Icons: keine
+- **Interaktionen (Actions):** onSave(input: LeadAntiSpamSaveInput) — mode (quarantine/spam), perSourcePerHourLimit, perIpPerHourLimit, anomalySpikeFactor, lostAutoDeleteDays, + Toggles disposableDomainEnabled/honeypotEnabled/fastSubmitEnabled (als „on")
+- **States:** Empty (kein Data → leerer Container-Shell) / Loading (`submitting` an Save-Button) / Permission-Denied (404 auf Route)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
diff --git a/Tickets/Interim-Handover/P17-HR.md b/Tickets/Interim-Handover/P17-HR.md
new file mode 100644
index 00000000..ca30e004
--- /dev/null
+++ b/Tickets/Interim-Handover/P17-HR.md
@@ -0,0 +1,279 @@
+# P17 · HR-Hub (AK-P17a/b → D-HR)
+
+> Interim-UI-Referenz (AD-28) für den HR-Hub. Alle Screens rein präsentational (VM als Prop, Route besitzt
+> Loader/Fetcher/Actions). **Layout = Davids freie Hand** — unten nur ELEMENTE/DATEN/ACTIONS. `~/ui` ist das
+> gesetzte Baukasten-Inventar; `data-ui-id`-Marker existieren für Interim-Snapshots. Route-URLs: App-Host,
+> Ordner `routes/app.$tenant/` → `/{tenant}/hr/…`.
+
+### HrHubPage · D-HR „HR-Hub-Start" (A1/A6)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrHubPage.tsx`
+- **Route(n):** `/{tenant}/hr` (`routes/app.$tenant/hr._index.tsx`)
+- **Daten (VM):** kein `@hivegrid`-VM — lokale Props `moduleActive: boolean`, `links: HrHubLink[]` (`{ labelKey, href }`); Loader gated Connector+Permission und liefert die Quick-Link-Liste
+- **Benötigte UI-Elemente (KEIN Layout):** Card, EmptyState · Icons: FeatherArrowRight, FeatherLock
+- **Interaktionen (Actions):** keine Callbacks — Links sind `` (Navigations-Kacheln)
+- **States:** Locked (`!moduleActive` → EmptyState mit Lock) / Empty (leere Link-Liste rendert leere Grid) / Permission-Denied via Loader-Gate (404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen (Hub-Widgets Ankündigungen/Pflichten/Geburtstage kommen laut Header erst als Folge-Nachzug):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrDirectoryPage · D-HR „MA-Verzeichnis" (A2)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrDirectoryPage.tsx`
+- **Route(n):** `/{tenant}/hr/directory` (`routes/app.$tenant/hr.directory._index.tsx`)
+- **Daten (VM):** `HrDirectoryVM` — `packages/view-models/src/hr/hr-directory.vm.ts` (nur nicht-sensible Anzeigefelder; Notfallkontakt NUR im Detail)
+- **Benötigte UI-Elemente (KEIN Layout):** Avatar, EmptyState, IconButton, Table · Icons: FeatherMail
+- **Interaktionen (Actions):** keine Action-Callbacks — Name-Link → Detail, `mailto:`-IconButton
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrDirectoryDetailPage · D-HR „MA-Detail" (A3)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrDirectoryDetailPage.tsx`
+- **Route(n):** `/{tenant}/hr/directory/{userId}` (`routes/app.$tenant/hr.directory.$userId.tsx`)
+- **Daten (VM):** kein VM-Typ-Import — flache Props (`name`, `email`, `position`, `team`, `isSelf`, `canRevealEmergency`, `canEditEmergency`, `hasEmergencyContact`, `revealedContact`, `revealing`, `saving`, `savingEmergency`); Domain in `hr-directory.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Button, Card, Dialog, TextInput · Icons: —
+- **Interaktionen (Actions):** `onSaveProfile({position,team})`, `onRevealEmergency()`, `onSetEmergency({name,phone,relation})`
+- **States:** Self-Edit vs. Read-only (`isSelf`) / Emergency: kein Kontakt / gespeichert-nicht-enthüllbar / enthüllt / Reveal-Button / Loading via `revealing`/`saving`/`savingEmergency` / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (On-Demand-Reveal Notfallkontakt, serverseitig auditiert):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrLeavePage · D-HR „Urlaub" (A2 + A4/A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrLeavePage.tsx`
+- **Route(n):** `/{tenant}/hr/leave` (`routes/app.$tenant/hr.leave._index.tsx`), URL-Tabs `?tab=own|approvals|team`
+- **Daten (VM):** `HrLeaveOwnVM`, `HrLeaveApproverVM`, `HrVacationRequestKind` — `packages/view-models/src/hr/hr-leave.vm.ts`; plus lokaler `HrLeaveTeamAbsence`
+- **Benötigte UI-Elemente (KEIN Layout):** Alert, BadgeFeedback, Button, Card, Dialog, EmptyState, Select, Tabs, TextArea, TextInput, `DateField` (tasks) · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onSubmit({requestKind,approverUserId,startDate,endDate,note})`, `onWithdraw(requestId)`, `onReply(requestId,reply)`, `onApprove(requestId)`, `onReject(requestId)`, `onClarify(requestId,question)`
+- **States:** Empty je Tab (own/approvals/team → EmptyState) / Loading (`status!=="ready"` → null) / Konflikt-Warnung (Alert bei `hasConflict`) / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Overlap-Konflikt als Inline-Warnung):
+  - [ ] Neue UI/UX-Entscheidungen (Approver-Auswahl aktuell freies TextInput = userId):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrSickPage · D-HR „Krank" (A2 + A4)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSickPage.tsx`
+- **Route(n):** `/{tenant}/hr/sick` (`routes/app.$tenant/hr.sick._index.tsx`), Tabs `own|office`
+- **Daten (VM):** `HrSickLeaveOwnVM`, `HrSickLeaveOfficeVM`, `HrSickLeaveRow` — `packages/view-models/src/hr/hr-sickleave.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Dialog, EmptyState, Tabs, TextInput, `DateField` (tasks) · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onReport({startDate,expectedReturnDate})`, `onReportFor({employeeUserId,startDate})`, `onAttach(reportId,certificateFileId)`, `onRevealCertificate(reportId)`, `onClose(reportId,returnDate)`
+- **States:** Empty je Tab (own/office → EmptyState) / AU-Zertifikat nie gerendert, nur Reveal-Action / Loading (Rows via `?? []`) / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Zertifikat sensibel, reveal-on-demand):
+  - [ ] Neue UI/UX-Entscheidungen (Attach = File-ID-TextInput; echte Datei-Auswahl offen):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrRequestsPage · D-HR „Anträge" (A2 + A4/A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrRequestsPage.tsx`
+- **Route(n):** `/{tenant}/hr/requests` (`routes/app.$tenant/hr.requests._index.tsx`), Tabs `own|approvals`
+- **Daten (VM):** `HrRequestOwnVM`, `HrRequestApproverVM`, `HrRequestKind` — `packages/view-models/src/hr/hr-request.vm.ts`; plus lokaler `HrRequestTemplateOption`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Dialog, EmptyState, Select, Tabs, TextArea, TextInput · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onSubmit({templateId,requestKind,approverUserId,title,details})`, `onWithdraw(requestId)`, `onApprove(requestId)`, `onReject(requestId)`, `onClarify(requestId,question)`
+- **States:** Empty je Tab (own/approvals → EmptyState) / Loading (Rows via `?? []`) / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (optionaler Vorlagen-Prefill):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrPayslipsPage · D-HR „Lohnabrechnungen" (A2)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrPayslipsPage.tsx`
+- **Route(n):** `/{tenant}/hr/payslips` (`routes/app.$tenant/hr.payslips._index.tsx`)
+- **Daten (VM):** `HrPayslipOwnVM` — `packages/view-models/src/hr/hr-payslip.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, EmptyState, Table · Icons: FeatherExternalLink
+- **Interaktionen (Actions):** `onOpen(payslipId)` (Öffnen serverseitig auditiert, Route öffnet File)
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Unread-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrMyDocumentsPage · D-HR „Eigene Dokumente" (A2)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrMyDocumentsPage.tsx`
+- **Route(n):** `/{tenant}/hr/my-documents` (`routes/app.$tenant/hr.my-documents.tsx`)
+- **Daten (VM):** `HrDocumentListVM`, `HrDocumentKind` — `packages/view-models/src/hr/hr-document.vm.ts`; plus injizierter `upload: HrDocumentUploadState` (idle/uploading/done/error)
+- **Benötigte UI-Elemente (KEIN Layout):** Alert, BadgeFeedback, Button, Dialog, EmptyState, Select, Table, TextInput; native `input[type=date]` + `input[type=file]` (bewusste Ausnahme) · Icons: FeatherUpload
+- **Interaktionen (Actions):** `onArchive(documentId)`, `onUpload({file,title,documentKind,validUntilIso})`, `onResetUpload()`
+- **States:** Empty (`rows.length===0` → EmptyState) / No-Results (Kind-Filter leer → EmptyState `no-results`) / Loading (`status!=="ready"` → leeres Div) / Upload uploading/error (Alert) / Read-only-Badge (Office-Docs) / Expired-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Kind-Filter client-seitig, Office-Docs read-only):
+  - [ ] Neue UI/UX-Entscheidungen (Upload-Progress-Darstellung):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAnnouncementsFeedPage · D-HR „Ankündigungen" (A2)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAnnouncementsFeedPage.tsx`
+- **Route(n):** `/{tenant}/hr/announcements` (`routes/app.$tenant/hr.announcements._index.tsx`), Filter `?unread=1`
+- **Daten (VM):** `HrAnnouncementFeedVM` — `packages/view-models/src/hr/hr-announcement.vm.ts` (pinned first)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Card, EmptyState, LinkButton, Tabs · Icons: FeatherPin
+- **Interaktionen (Actions):** keine Callbacks — Zeilen-Link → Detail, „Verwalten"-Link (`manageHref`, bei `canManage`)
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Ack-Required-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (pinned zuerst, Unread-Filter):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAnnouncementDetailPage · D-HR „Ankündigung Detail" (A3)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAnnouncementDetailPage.tsx`
+- **Route(n):** `/{tenant}/hr/announcements/{id}` (`routes/app.$tenant/hr.announcements.$id.tsx`)
+- **Daten (VM):** `HrAnnouncementDetailVM` — `packages/view-models/src/hr/hr-announcement.vm.ts` (Markdown-Body, Reaktionen, Kommentare 1 Ebene, Read-Receipts)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Table, TextInput · Icons: — (Emoji-Reaktionen 👍❤️🎉👀 als Literale)
+- **Interaktionen (Actions):** `onAck()`, `onReact(reaction)`, `onComment(body)`
+- **States:** Loading (`status!=="ready"` → leeres Div) / Kommentare-Empty (`commentEmptyKey`) / Ack-Zustand acknowledged vs. Button / Read-Receipt-Tabelle nur bei `canManage` / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Markdown wird aktuell `whitespace-pre-wrap` roh gerendert):
+  - [ ] Neue UI/UX-Entscheidungen (Reaktions-Set fix vier Emojis):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrLocationsPage · D-HR „Standorte" (A2 + A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrLocationsPage.tsx`
+- **Route(n):** `/{tenant}/hr/locations` (`routes/app.$tenant/hr.locations._index.tsx`)
+- **Daten (VM):** `HrLocationListVM` — `packages/view-models/src/hr/hr-location.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Checkbox, Dialog, EmptyState, Table, TextInput · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onCreate({name,city,isDefault})`, `onSetDefault(locationId)`, `onArchive(locationId)`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Default-Badge / Aktionen nur bei `canManage` / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrPasswordSafeListPage · D-HR „Passwort-Safe" (A2 + A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrPasswordSafeListPage.tsx`
+- **Route(n):** `/{tenant}/hr/password-safe` (`routes/app.$tenant/hr.password-safe._index.tsx`)
+- **Daten (VM):** `VaultListVM` — `packages/view-models/src/hr/hr-password-safe.vm.ts` (nur zugriffsberechtigte Vaults, keine Secrets)
+- **Benötigte UI-Elemente (KEIN Layout):** Button, Card, Dialog, EmptyState, TextInput · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onCreateVault({name,description})`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Create nur bei `canCreate` / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrVaultDetailPage · D-HR „Passwort-Safe Detail" (A3 + reveal)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrVaultDetailPage.tsx`
+- **Route(n):** `/{tenant}/hr/password-safe/{vaultId}` (`routes/app.$tenant/hr.password-safe.$vaultId._index.tsx`); Action-only: `…$vaultId.reveal.tsx`, `…$vaultId.export.tsx`
+- **Daten (VM):** `VaultDetailVM`, `EntryRow` — `packages/view-models/src/hr/hr-password-safe.vm.ts` (nur `hasUsername/hasPassword/hasNotes`-Flags, `copyAutoClearMs`)
+- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Button, Card, CopyToClipboardButton, Dialog, EmptyState, LinkButton, Select, Tag_Label, TextInput · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onReveal(entryId,field): Promise`, `onCreateEntry({title,url,username,password,notes})`, `onArchiveEntry(entryId)`, `onGrantAccess({targetUserId,permissionLevel})`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Secret pro Feld reveal-on-demand → copy → Clipboard-Wipe nach 30s (DoD #3) / Aktionen gated via `canWrite`/`canManageAccess` / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (SECURITY: Werte nur per audited reveal, Clipboard-Auto-Clear):
+  - [ ] Neue UI/UX-Entscheidungen (Copy-Cleared-Hinweis, Zugriff via userId-TextInput):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrSurveysListPage · D-HR „Umfragen" (A2 + A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSurveysListPage.tsx`
+- **Route(n):** `/{tenant}/hr/surveys` (`routes/app.$tenant/hr.surveys._index.tsx`)
+- **Daten (VM):** `HrSurveyListVM` — `packages/view-models/src/hr/hr-survey.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Dialog, EmptyState, LinkButton, Select, TextInput · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onCreate({title,description,isAnonymous,resultVisibility})`, `onSetStatus(surveyId,"open"|"close")`, `onArchive(surveyId)`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (Rows via `?? []`) / Status-/Anonymous-Badges / Teilnehmen/Ergebnisse/Edit gated (`hasSubmitted`,`canViewResults`,`canManage`) / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrSurveyParticipationPage · D-HR „Umfrage Teilnahme" (A3/A4)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSurveyParticipationPage.tsx`
+- **Route(n):** `/{tenant}/hr/surveys/{id}` (`routes/app.$tenant/hr.surveys.$id.tsx`)
+- **Daten (VM):** `HrSurveyParticipationVM`, `HrSurveyItem`, `HrSurveyItemType` — `packages/view-models/src/hr/hr-survey.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** Alert, Button, Card, Checkbox, Dialog, Select, TextInput; native `input[type=date]` (sanktionierte Ausnahme) · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onSubmit(answers: Record)`, `onAddItem({itemType,prompt,options,required})`, `onRemoveItem(itemId)`
+- **States:** Loading (`status!=="ready"` → leeres Div) / Anonymous-Hinweis (Alert) / bereits abgegeben (Alert success) / offen → Formular / geschlossen (Alert neutral) / Item-Editor nur bei `canManage` / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Item-Typen text/rating_5/nps/choice/boolean/date):
+  - [ ] Neue UI/UX-Entscheidungen (Antwort-Feld-Rendering je Typ):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrSurveyResultsPage · D-HR „Umfrage Ergebnisse" (A3)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSurveyResultsPage.tsx`
+- **Route(n):** `/{tenant}/hr/surveys/{id}/results` (`routes/app.$tenant/hr.surveys.$id.results.tsx`)
+- **Daten (VM):** `HrSurveyResultsVM` — `packages/view-models/src/hr/hr-survey.vm.ts` (Verteilung + Durchschnitt + Freitext)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Card, EmptyState, Progress · Icons: —
+- **Interaktionen (Actions):** keine (read-only)
+- **States:** Empty (`submissionCount===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Anonymous-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Interim nutzt Progress-Balken; „richer charts" = expliziter Nachzug lt. Header):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAdminAnnouncementsPage · D-HR „Ankündigungen verwalten" (A2 + A4/A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminAnnouncementsPage.tsx`
+- **Route(n):** `/{tenant}/hr/admin/announcements` (`routes/app.$tenant/hr.admin.announcements._index.tsx`)
+- **Daten (VM):** kein `@hivegrid`-VM — lokale Props `rows: AdminAnnouncementRow[]`, `audiences: string[]`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Dialog, EmptyState, Select, TextArea, TextInput, `DateField` (tasks) · Icons: FeatherPin, FeatherPlus
+- **Interaktionen (Actions):** `onCreate({title,body,audienceKind,audienceRef,requiresAck,pinned,publishAt,expiresAt})`, `onArchive(announcementId)`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (—, Rows als Prop) / Ack-/Pin-/Audience-Badges / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Audience, Ack, Pin, Schedule/Expiry):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAdminPasswordSafePage · D-HR „Passwort-Safe-Verwaltung" (A2/A6)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminPasswordSafePage.tsx`
+- **Route(n):** `/{tenant}/hr/admin/password-safe` (`routes/app.$tenant/hr.admin.password-safe._index.tsx`)
+- **Daten (VM):** `AdminVaultListVM` — `packages/view-models/src/hr/hr-password-safe.vm.ts` (alle Tenant-Vaults, Entry-/Access-Counts, NIE Inhalte)
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, EmptyState, Table · Icons: —
+- **Interaktionen (Actions):** `onArchiveVault(vaultId)`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Active/Archived-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Oversight ohne Content-Zugriff):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAdminPayslipsPage · D-HR „Lohnabrechnungen verwalten" (A2 + A4)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminPayslipsPage.tsx`
+- **Route(n):** `/{tenant}/hr/admin/payslips` (`routes/app.$tenant/hr.admin.payslips._index.tsx`)
+- **Daten (VM):** kein `@hivegrid`-VM — lokale Props `rows: PayslipAdminRow[]`, `preview: PayslipPreviewRow[]` (confidence hard/soft/none), `period: {year,month}`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, EmptyState, Table, TextArea, TextInput · Icons: —
+- **Interaktionen (Actions):** `onMatch({periodYear,periodMonth,files})`, `onConfirm({fileId,employeeUserId,periodYear,periodMonth})`
+- **States:** Empty (existing `rows.length===0` → EmptyState) / Preview-Block nur bei `preview.length>0` / Loading (Props) / Confidence-Badges / Read/Unread-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Bulk-Auto-Match-Preview + Per-Row-Confirm):
+  - [ ] Neue UI/UX-Entscheidungen (Files-Input als Freitext-Hint, Assignee als userId-TextInput):
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAdminRequestTemplatesPage · D-HR „Antragsvorlagen" (A2 + A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminRequestTemplatesPage.tsx`
+- **Route(n):** `/{tenant}/hr/admin/request-templates` (`routes/app.$tenant/hr.admin.request-templates._index.tsx`)
+- **Daten (VM):** `HrRequestTemplateListVM`, `HrRequestKind` — `packages/view-models/src/hr/hr-request.vm.ts`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Dialog, EmptyState, Select, TextInput · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onCreate({requestKind,defaultApproverUserId,title,description})`, `onToggle(templateId,isActive)`, `onArchive(templateId)`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (Rows via `?? []`) / Inactive-Badge / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte:
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev:
+
+### HrAdminUploadRemindersPage · D-HR „Upload-Erinnerungen" (A2 + A5)
+- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminUploadRemindersPage.tsx`
+- **Route(n):** `/{tenant}/hr/admin/upload-reminders` (`routes/app.$tenant/hr.admin.upload-reminders._index.tsx`)
+- **Daten (VM):** `HrReminderListVM` — `packages/view-models/src/hr/hr-upload-reminder.vm.ts`; `HrReminderCadenceKind` aus `packages/view-models/src/hr/reminder-cadence.ts`; plus Props `documentKinds`, `audiences`
+- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Dialog, EmptyState, Select, TextInput, `DateField` (tasks) · Icons: FeatherPlus
+- **Interaktionen (Actions):** `onCreate({title,documentKind,cadenceKind,intervalMonths,audienceKind,dueAt,mandatory,escalate})`, `onSetStatus(reminderId,"active"|"paused")`, `onArchive(reminderId)`
+- **States:** Empty (`rows.length===0` → EmptyState) / Loading (Rows via `?? []`) / Status-Badge active/paused / Permission-Denied (Loader/404)
+- **Davids Delta (offen):**
+  - [ ] Überschriebene/geänderte Spec-Punkte (Cadence once/yearly/every_n_months/on_expiry; Dispatch im Worker-Cron):
+  - [ ] Neue UI/UX-Entscheidungen:
+  - [ ] Subframe-Screen-Link(s):
+  - [ ] Offene Fragen an Dev: