diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
index 389ab11a..a2d9d227 100644
--- a/.gitea/workflows/ci.yml
+++ b/.gitea/workflows/ci.yml
@@ -42,7 +42,7 @@ jobs:
- name: Upload screenshots
if: always()
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: unit-test-screenshots
path: frontend/test-results/screenshots/
diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md
index 551a1f34..47b2d1b9 100644
--- a/frontend/CLAUDE.md
+++ b/frontend/CLAUDE.md
@@ -35,24 +35,40 @@ src/
│ ├── api/ # Internal API proxies (server-side only)
│ ├── login/ logout/ # Auth pages
│ └── ...
-├── lib/
-│ ├── components/ # Reusable Svelte components
-│ │ ├── document/ # Document-specific components
-│ │ ├── chronik/ # Activity feed components
-│ │ └── user/ # User-related components
+├── lib/ # Domain-based package structure (mirrors backend)
+│ ├── document/ # Document domain: components, stores, services, utils
+│ │ ├── annotation/ # Annotation overlay components
+│ │ ├── comment/ # Comment thread components
+│ │ └── transcription/ # Transcription editor + block logic
+│ ├── person/ # Person domain: chips, typeahead, avatar, format
+│ │ ├── relationship/ # Relationship form + chip components
+│ │ └── genealogy/ # Stammbaum (family tree) components
+│ ├── tag/ # Tag domain: TagInput, TagChipList, TagParentPicker
+│ ├── geschichte/ # Geschichte (story) domain: editor + card
+│ ├── notification/ # Notification bell + dropdown + store
+│ ├── activity/ # Activity feed (Chronik) components
+│ ├── conversation/ # Bilateral conversation (Briefwechsel) components
+│ ├── ocr/ # OCR progress, training cards, trigger
+│ ├── user/ # User profile/password/groups section components
+│ ├── shared/ # Cross-domain utilities and primitives
+│ │ ├── actions/ # Svelte actions (clickOutside, etc.)
+│ │ ├── hooks/ # Reusable Svelte state hooks (useTypeahead, etc.)
+│ │ ├── server/ # Server-only utilities (locale, session)
+│ │ ├── services/ # Client-side service helpers
+│ │ ├── utils/ # Pure utility functions (date, search, etc.)
+│ │ ├── primitives/ # Generic UI primitives (BackButton, ProgressRing, etc.)
+│ │ ├── dashboard/ # Dashboard stat components
+│ │ ├── discussion/ # CommentThread + shared discussion UI
+│ │ ├── help/ # Help/FAQ page components
+│ │ ├── api.server.ts # Typed API client factory
+│ │ ├── errors.ts # Error code mapping (mirrors backend ErrorCode)
+│ │ ├── types.ts # Shared TypeScript types
+│ │ ├── relativeTime.ts # Relative time formatting
+│ │ └── utils.ts # Top-level shared utilities
│ ├── generated/ # Auto-generated API types (openapi-typescript)
-│ ├── server/ # Server-only utilities (db, auth helpers)
-│ ├── services/ # Client-side service logic
-│ ├── stores/ # Svelte stores (global state)
-│ ├── types.ts # Shared TypeScript types
-│ ├── errors.ts # Error code mapping (mirrors backend ErrorCode)
-│ ├── api.server.ts # Typed API client factory
-│ ├── utils.ts # Shared utilities
-│ ├── relativeTime.ts # Time formatting
-│ ├── search.ts # Search utilities
│ └── paraglide/ # Generated i18n code
├── hooks/ # SvelteKit hooks (handle, handleFetch)
-└── actions/ # Custom Svelte actions (click outside, etc.)
+└── ... # Other SvelteKit config files
```
## API Client Pattern
@@ -130,14 +146,15 @@ Card pattern for content sections:
## Key UI Components
-| Component | Props | Description |
-| -------------------- | ---------------------------------------------------- | ------------------------------------- |
-| `PersonTypeahead` | `name`, `label`, `value`, `initialName`, `on:change` | Single-person selector with typeahead |
-| `PersonMultiSelect` | `selectedPersons` (bind) | Chip-based multi-person selector |
-| `TagInput` | `tags` (bind), `allowCreation?`, `on:change` | Tag chip input with typeahead |
-| `PdfViewer` | `url`, `annotations`, `on:annotation` | PDF rendering with annotation overlay |
-| `TranscriptionBlock` | `block`, `mode` | Read/edit transcription block |
-| `DocumentTopBar` | `document` | Responsive document metadata header |
+| Component | Location | Props | Description |
+| -------------------- | ------------------------------ | --------------------------------------- | ------------------------------------------ |
+| `PersonTypeahead` | `$lib/person/` | `name`, `label`, `value`, `initialName` | Single-person selector with typeahead |
+| `PersonMultiSelect` | `$lib/person/` | `selectedPersons` (bind) | Chip-based multi-person selector |
+| `TagInput` | `$lib/tag/` | `tags` (bind), `allowCreation?` | Tag chip input with typeahead |
+| `PdfViewer` | `$lib/document/` | `url`, `annotations` | PDF rendering with annotation overlay |
+| `TranscriptionBlock` | `$lib/document/transcription/` | `block`, `mode` | Read/edit transcription block |
+| `DocumentTopBar` | `$lib/document/` | `document` | Responsive document metadata header |
+| `BackButton` | `$lib/shared/primitives/` | — | Calls `history.back()`; 44 px touch target |
## How to Run
diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts
index cd16673b..917ed953 100644
--- a/frontend/src/hooks.server.ts
+++ b/frontend/src/hooks.server.ts
@@ -3,7 +3,7 @@ import { paraglideMiddleware } from '$lib/paraglide/server';
import { sequence } from '@sveltejs/kit/hooks';
import { env } from 'process';
import { cookieName, cookieMaxAge } from '$lib/paraglide/runtime';
-import { detectLocale } from '$lib/server/locale';
+import { detectLocale } from '$lib/shared/server/locale';
const PUBLIC_PATHS = ['/login', '/logout', '/forgot-password', '/reset-password', '/register'];
diff --git a/frontend/src/lib/components/chronik/ChronikEmptyState.svelte b/frontend/src/lib/activity/ChronikEmptyState.svelte
similarity index 100%
rename from frontend/src/lib/components/chronik/ChronikEmptyState.svelte
rename to frontend/src/lib/activity/ChronikEmptyState.svelte
diff --git a/frontend/src/lib/components/chronik/ChronikEmptyState.svelte.spec.ts b/frontend/src/lib/activity/ChronikEmptyState.svelte.spec.ts
similarity index 100%
rename from frontend/src/lib/components/chronik/ChronikEmptyState.svelte.spec.ts
rename to frontend/src/lib/activity/ChronikEmptyState.svelte.spec.ts
diff --git a/frontend/src/lib/components/chronik/ChronikErrorCard.svelte b/frontend/src/lib/activity/ChronikErrorCard.svelte
similarity index 100%
rename from frontend/src/lib/components/chronik/ChronikErrorCard.svelte
rename to frontend/src/lib/activity/ChronikErrorCard.svelte
diff --git a/frontend/src/lib/components/chronik/ChronikErrorCard.svelte.spec.ts b/frontend/src/lib/activity/ChronikErrorCard.svelte.spec.ts
similarity index 100%
rename from frontend/src/lib/components/chronik/ChronikErrorCard.svelte.spec.ts
rename to frontend/src/lib/activity/ChronikErrorCard.svelte.spec.ts
diff --git a/frontend/src/lib/components/chronik/ChronikFilterPills.svelte b/frontend/src/lib/activity/ChronikFilterPills.svelte
similarity index 100%
rename from frontend/src/lib/components/chronik/ChronikFilterPills.svelte
rename to frontend/src/lib/activity/ChronikFilterPills.svelte
diff --git a/frontend/src/lib/components/chronik/ChronikFilterPills.svelte.spec.ts b/frontend/src/lib/activity/ChronikFilterPills.svelte.spec.ts
similarity index 100%
rename from frontend/src/lib/components/chronik/ChronikFilterPills.svelte.spec.ts
rename to frontend/src/lib/activity/ChronikFilterPills.svelte.spec.ts
diff --git a/frontend/src/lib/components/chronik/ChronikFuerDichBox.svelte b/frontend/src/lib/activity/ChronikFuerDichBox.svelte
similarity index 95%
rename from frontend/src/lib/components/chronik/ChronikFuerDichBox.svelte
rename to frontend/src/lib/activity/ChronikFuerDichBox.svelte
index 05d58e56..14a440f1 100644
--- a/frontend/src/lib/components/chronik/ChronikFuerDichBox.svelte
+++ b/frontend/src/lib/activity/ChronikFuerDichBox.svelte
@@ -1,8 +1,8 @@