# SupplierPulse – UI/UX & Design-System-Optimierung

> **Working mode**: Plan first. NO code before plan approval. Blunt feedback over diplomacy. Small iterative PRs. Treat this prompt as a feature brief, NOT as a step-by-step recipe — push back where it makes sense.

> **Source of truth**: README.md (CLAUDE.md is stale). Vor JEDER Arbeit: `/investigate` der bestehenden CI-Doku im Repo (Suche in `docs/`, `brand/`, `BMW-CI*`, `design-system*`, `tokens*`, `tailwind.config*`). Falls eine CI-Doku gefunden wird, **liest** Claude diese vollständig und **stimmt diesen Prompt damit ab** — bei Konflikten gewinnt die CI-Doku, dieser Brief wird angepasst, NICHT die CI.

> **Was dieser Prompt NICHT ist**: ein Feature-Add. Das ist ein Quality-Sweep über die bestehende Codebase. Keine neuen Module, keine neue Logik. Konsistenz, Aufräumen, Token-Disziplin.

---

## 0. Kontext

SupplierPulse hat heute drei Probleme, die das Tool unprofessioneller wirken lassen als es technisch ist:

1. **Dark Mode ist inkonsistent** — Komponenten kippen teilweise nicht mit, weil hardcoded Farben (`bg-white`, `text-gray-900`, `#fff`, `#000`, Tailwind-Default-Grays) statt CSS-Variablen verwendet werden.
2. **Typografie ist Mischmasch** — System-Fonts, Google Fonts und ggf. fragmentarisch eingebaute BMW-Fonts vermischen sich. Es muss durchgängig die offizielle BMW-Schriftfamilie verwendet werden, **lokal gehostet** (kein Google-Fonts-CDN, keine externen Aufrufe).
3. **UI ist stellenweise überfüllt / nicht selbsterklärend** — fehlende Tooltips, zu viele Buttons gleichzeitig, fehlende Spacing-Disziplin, Tabellen ohne Hover-Affordances.

Ziel dieser Refaktorierung: Ein konsistentes Design-System aus Tokens (Farbe, Typografie, Spacing, Radius, Shadow, Motion) + ein vollständiges Audit der bestehenden Komponenten gegen dieses System + sauberer Dark Mode + lokale BMW-Fonts.

---

## 1. Hartes Push-Back (Kais hat das so gewollt, ich nicht — bitte vor Umsetzung kurz mit Kais klären)

1. **„Alle 20 Font-Dateien einbinden"** ist eine schlechte Idee. Das Set enthält redundante Schnitte (z. B. `BMWGroupTN Condensed Thin Bold` als einzige Variante einer ganzen Familie — kein Regular dazu, also unbrauchbar als Stand-Alone-Familie). Empfohlenes Minimum (7 Dateien, ~600 KB nach woff2+Subset): siehe Abschnitt 3.2. Falls Kais wirklich alle 20 will: das Argument muss konkret sein („wir brauchen Italic in TN für X"), sonst bleibt es bei 7.
2. **Reine „Theme-Variable umfärben"-PRs lösen Dark-Mode-Inkonsistenz NICHT.** Das ist ein Token-Disziplin-Problem, kein Farbproblem. Ohne Lint-Regel + Audit kehrt das Problem bei jeder neuen Komponente wieder. Lint-Regel ist Teil dieses Prompts, nicht verhandelbar.
3. **„BMW CI ist im Projekt hochgeladen, suche diese"** — `/investigate` zwingend. Wird die CI-Doku gefunden, ist **sie** die Quelle für Farb-/Typo-Werte, nicht dieser Brief. Was hier steht, sind verifizierte Werte aus dem User-Memory (BMW-Blau `#0066B1`, BMW-Dunkelblau `#003D6B`, Grau-700 `#6f6f6f`, Grau-200 `#e5e5e5`) — wenn die CI-Doku abweicht, gewinnt die CI-Doku.

---

## 2. Scope

| Im Scope | Außerhalb |
|---|---|
| Design-Tokens (Farbe, Typo, Spacing, Radius, Shadow, Motion) als CSS-Variablen | Neue Features oder Module |
| Lokale BMW-Font-Integration (woff2, subsetted) | Logo-Design, Icon-Set-Wechsel |
| Dark-Mode-Audit + Fix sämtlicher Inkonsistenzen | Komplettes Re-Design einzelner Module |
| Tooltip-System (Mouseover-Erläuterungen, bilingual, konfigurierbar) | Inhalt einzelner Tooltip-Texte (außer Default-Set) |
| Lint-Regel gegen hardcoded Farben/Sizes | Tailwind-Plugin-Entwicklung |
| Komponenten-Audit (Tabellen, Buttons, Forms, Modals, Cards) | Refactoring von Business-Logik |
| Spacing- und Layout-Konsolidierung | Performance-Optimierungen |
| A11y-Basics (Kontrast WCAG AA, Fokus-Sichtbarkeit, ARIA-Labels) | Volle WCAG-AAA-Compliance |

---

## 3. Design-Tokens

### 3.1 Farbpalette

**Quelle**: BMW CI im Repo (zwingend lesen). Fallback-Werte aus User-Memory (verifizieren, nicht blind übernehmen):

```
Brand
├── brand-primary       #0066B1   (BMW-Blau)
├── brand-primary-dark  #003D6B   (BMW-Dunkelblau)
└── brand-accent        ?         (aus CI-Doku, nicht raten)

Neutrals (light mode base)
├── neutral-0           #FFFFFF
├── neutral-50          #F7F7F7
├── neutral-100         #EFEFEF
├── neutral-200         #E5E5E5   (verifiziert)
├── neutral-300         #D4D4D4
├── neutral-400         #A3A3A3
├── neutral-500         #737373
├── neutral-600         #525252
├── neutral-700         #6F6F6F   (verifiziert)
├── neutral-800         #2A2A2A
├── neutral-900         #171717
└── neutral-1000        #000000

Semantic
├── success             aus CI, sonst #22A06B
├── warning             aus CI, sonst #F5A623
├── danger              aus CI, sonst #D0342C
└── info                = brand-primary
```

**Kritisch**: Die Tokens sind in `--color-*`-CSS-Variablen abgelegt, NICHT als Tailwind-Custom-Colors hardcoded. Tailwind referenziert die Variablen (`theme.extend.colors.brand-primary = 'var(--color-brand-primary)'`). Das ist der EINZIGE Weg, Dark Mode sauber zu machen.

### 3.2 Semantic Color Tokens (Light + Dark)

Komponenten verwenden ausschließlich semantische Tokens, niemals neutral-N direkt. Das ist die wichtigste Regel des ganzen PRs.

```
Light Mode                          Dark Mode
─────────────────────────           ────────────────────────
--bg-canvas:    neutral-0           neutral-900
--bg-surface:   neutral-0           neutral-800
--bg-elevated:  neutral-50          neutral-700
--bg-muted:     neutral-100         neutral-800
--bg-overlay:   rgba(0,0,0,.5)      rgba(0,0,0,.7)

--fg-default:   neutral-900         neutral-50
--fg-muted:     neutral-600         neutral-300
--fg-subtle:    neutral-500         neutral-400
--fg-on-brand:  neutral-0           neutral-0

--border-default: neutral-200       neutral-700
--border-strong:  neutral-300       neutral-600
--border-focus:   brand-primary     brand-primary

--state-hover:   neutral-100        neutral-700
--state-active:  neutral-200        neutral-600
--state-selected: brand-primary/10  brand-primary/20
```

**Komponenten greifen nur auf `--bg-surface`, `--fg-default` etc. zu — NIE auf `--color-neutral-50` direkt.** Das ist die Token-Hierarchie, die Dark Mode robust macht.

### 3.3 Typografie (BMW-Font-System)

#### 3.3.1 Lokale Font-Einbindung

**Empfohlenes Set (7 Dateien, woff2, subsetted auf Latin + Latin-Ext, ~600 KB total):**

| Token | Familie | Schnitt | Datei | Anwendung |
|---|---|---|---|---|
| `--font-display` | BMWGroupTN | Bold | BMWGroupTN-Bold | H1, H2, Login-Screens, Headlines |
| `--font-sans` | BMW Group Light | Regular | BMWGroupLight-Regular | Body, Default-Lesetexte |
| `--font-sans` | BMW Group Light | Bold | BMWGroupLight-Bold | Body-Akzente, Labels |
| `--font-ui` | BMW Group | Regular | BMWGroup-Regular | Buttons, Forms, Menü, UI-Elemente |
| `--font-ui` | BMW Group | Bold | BMWGroup-Bold | Button-Active, aktive Tabs |
| `--font-mono`-Ersatz / Tabellen | BMW Group Condensed | Regular | BMWGroupCondensed-Regular | Tabellen mit vielen Spalten (QAF, Workshop) |
| `--font-mono`-Ersatz / Tabellen | BMW Group Condensed | Bold | BMWGroupCondensed-Bold | Tabellen-Header |

**Italic** (Body-Italic + UI-Italic) muss vorhanden sein, sobald irgendwo `<em>` oder `font-style: italic` gebraucht wird. Setze entsprechend `BMWGroup-Italic` + `BMWGroupLight-Italic` mit ein wenn nötig (+2 Dateien = ~250 KB).

**Tatsächliche Code-Zahlen (echt, kein Bullshit)**:

Quelldateien aus `/BMW_Fonts.zip` (Originalgröße TTF/OTF unkomprimiert):
- BMW Group Regular: 124 KB → woff2 ca. 50 KB → +Subset Latin ca. 30 KB
- BMW Group Bold: 116 KB → woff2 ca. 50 KB → +Subset ca. 28 KB
- BMW Group Light Regular: 140 KB → woff2 ca. 60 KB → +Subset ca. 35 KB
- BMW Group Light Bold: 116 KB → woff2 ca. 50 KB → +Subset ca. 28 KB
- BMW Group Condensed Regular: 58 KB → woff2 ca. 25 KB → +Subset ca. 15 KB
- BMW Group Condensed Bold: 60 KB → woff2 ca. 25 KB → +Subset ca. 15 KB
- BMWGroupTN Bold: 142 KB → woff2 ca. 60 KB → +Subset ca. 35 KB
- → 7 Dateien, **~186 KB** preloadable

Bei vollem Set (20 Dateien) wären es ~1,1 MB nach woff2, das ist für eine Internal-Tool-UI akzeptabel aber unnötig.

#### 3.3.2 Build-Pipeline für Fonts

1. Originale TTF/OTF in `public/fonts/source/` (gitignored, nur lokal — diese Dateien sind ggf. BMW-lizenz-relevant, **CI/Lieferanten-Lizenz im Repo dokumentieren**, siehe 3.3.6).
2. Build-Script `scripts/fonts/build.mjs` mit `fonttools` (Python) ODER `wawoff2` + `subset-font` (Node):
   - Konvertiert zu woff2
   - Subsettet auf `U+0020-007F, U+00A0-024F, U+1E00-1EFF, U+2000-206F, U+20A0-20CF` (Latin, Latin Ext, Latin Ext Add, Punctuation, Currency)
   - Output: `public/fonts/dist/*.woff2`
3. Build-Script ist Teil des `pnpm build` (Pre-Build-Hook) ODER manuell ausführbar als `pnpm fonts:build`. Entscheidung im Plan-Doc.
4. `.gitignore` für `/source/` + `/dist/` (Build-Artifact), ABER: Geprüft, ob die Lizenz das erlaubt — wenn die Fonts proprietär sind und nur lokal sein dürfen, müssen sie ins Repo (mit klarem LICENSE-Hinweis).

#### 3.3.3 @font-face-Regeln + Tailwind-Anbindung

```css
@font-face {
  font-family: 'BMW Group';
  src: url('/fonts/dist/BMWGroup-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* … analog für weitere Schnitte */
```

Tailwind-Config:
```js
theme: {
  fontFamily: {
    display: ['BMWGroupTN', 'BMW Group', 'system-ui', 'sans-serif'],
    sans:    ['BMW Group Light', 'BMW Group', 'system-ui', 'sans-serif'],
    ui:      ['BMW Group', 'system-ui', 'sans-serif'],
    condensed: ['BMW Group Condensed', 'BMW Group', 'system-ui', 'sans-serif'],
  }
}
```

`font-display: swap` ist kritisch — verhindert FOIT (Flash of Invisible Text). System-Font wird sofort gerendert, BMW-Font swappt rein wenn geladen.

#### 3.3.4 Type-Scale (Tokens)

```
--text-xs:   12px / 1.4
--text-sm:   14px / 1.5
--text-base: 16px / 1.5   ← Body Default
--text-md:   18px / 1.5
--text-lg:   20px / 1.4
--text-xl:   24px / 1.3
--text-2xl:  32px / 1.2
--text-3xl:  40px / 1.15
--text-display: 56px / 1.1  ← nur Login/Marketing
```

Plus `letter-spacing`-Tokens (`--tracking-tight`, `--tracking-normal`, `--tracking-wide`) und `font-weight`-Tokens (`--weight-light: 300`, `--weight-regular: 400`, `--weight-bold: 700`).

#### 3.3.5 Hierarchie-Regeln

- **H1 — H3**: `font-display` (BMWGroupTN Bold)
- **H4 — H6, Labels**: `font-ui` Bold (BMW Group Bold)
- **Body, Paragraphen**: `font-sans` Regular (BMW Group Light Regular)
- **UI (Buttons, Tabs, Menüs, Inputs)**: `font-ui` Regular (BMW Group Regular)
- **Tabellen-Daten**: `font-condensed` Regular (mehr Spalten passen rein)
- **Tabellen-Header**: `font-condensed` Bold
- **Code/Mono**: System-Default `ui-monospace, SFMono-Regular, …` (BMW hat keinen Mono-Font; nicht erfinden)
- **Zahlen in Finanz-/KPI-Karten**: `font-ui` mit `font-variant-numeric: tabular-nums` für vertikales Alignment

#### 3.3.6 Lizenz-Doku

In `public/fonts/LICENSE.md` ablegen: Herkunft, Verwendungsbeschränkungen, Hinweis dass die Fonts nur für interne BMW-Tooling-Nutzung gedacht sind. Nicht halluzinieren — wenn Kais nicht weiß was die Lizenz erlaubt, **fragt Claude bei Kais nach** und legt erst dann das LICENSE-File an.

### 3.4 Spacing, Radius, Shadow, Motion

```
Spacing (4px-Grid)
--space-0 / 1 / 2 / 3 / 4 / 5 / 6 / 8 / 10 / 12 / 16 / 20 / 24
        0   4   8   12  16  20  24  32  40  48  64  80  96  px

Radius
--radius-xs:  2px
--radius-sm:  4px
--radius-md:  6px       ← Default für Buttons, Inputs
--radius-lg:  8px       ← Cards
--radius-xl:  12px      ← Modals
--radius-full: 9999px   ← Pills, Avatars

Shadow (light mode)
--shadow-xs:  0 1px 2px rgba(0,0,0,.05)
--shadow-sm:  0 2px 4px rgba(0,0,0,.06)
--shadow-md:  0 4px 12px rgba(0,0,0,.08)
--shadow-lg:  0 12px 32px rgba(0,0,0,.12)

Shadow (dark mode) — flacher, mehr Glow, weniger Schwarz
--shadow-xs:  0 1px 2px rgba(0,0,0,.4)
--shadow-sm:  0 2px 4px rgba(0,0,0,.5)
--shadow-md:  0 4px 12px rgba(0,0,0,.6)
--shadow-lg:  0 12px 32px rgba(0,0,0,.7)

Motion
--ease-out:   cubic-bezier(.16,1,.3,1)
--ease-inout: cubic-bezier(.65,0,.35,1)
--duration-fast: 120ms      ← Hover-Transitions
--duration-base: 200ms      ← Modal-Open
--duration-slow: 400ms      ← Page-Transitions
```

---

## 4. Dark Mode — systematischer Fix

### 4.1 Audit-Schritt 1: Inventur (PR-A Teil 1)

Lauf einen Codebase-Scan mit `grep`/`ripgrep` und liefere als Plan-Output eine Tabelle:

| Pattern | Anzahl | Beispiel-Dateien |
|---|---|---|
| Hardcoded Hex außerhalb `tailwind.config` und `tokens.css` | ? | ? |
| Tailwind `bg-white`, `bg-black`, `bg-gray-{50..900}`, `text-gray-…`, `border-gray-…` | ? | ? |
| Inline `style={{ color: '...' }}` | ? | ? |
| `dark:`-Variants die NICHT auf semantische Token greifen | ? | ? |

Ohne diese Inventur kein Token-Migration-PR. Sonst wird's Stochern im Nebel.

### 4.2 Audit-Schritt 2: Token-Migration (PR-B)

Jedes Vorkommen aus der Inventur wird ersetzt:

```
bg-white            → bg-surface       (CSS: var(--bg-surface))
bg-gray-50          → bg-elevated
bg-gray-100         → bg-muted
text-gray-900       → text-default     (CSS: var(--fg-default))
text-gray-600       → text-muted
text-gray-500       → text-subtle
border-gray-200     → border-default
border-gray-300     → border-strong
hover:bg-gray-100   → hover:bg-state-hover
```

`tailwind.config.ts` bekommt entsprechende `colors`-Mappings, sodass diese Klassen-Namen real existieren.

### 4.3 Theme-Switching

- **next-themes** verwenden (falls noch nicht vorhanden), nicht selbst basteln.
- Theme-Toggle ist im User-Menü (oben rechts), drei Optionen: `Light` / `Dark` / `System`.
- Persistierung via `localStorage`, zusätzlich serverseitig in `user_preferences.theme` (Supabase), Sync bei Login.
- SSR-fest: kein FOUC (Flash of Unstyled Content) beim ersten Render — Cookie- oder Server-Hint nutzen.

### 4.4 Dark-Mode-Falle: Bilder/Logos

- BMW-Logo: Light-Version (weiß auf Blau) und Dark-Version (Blau auf Weiß), wechselt per CSS.
- Charts (Recharts in Modul LSC): Achsen-, Grid-, Label-Farben aus Tokens lesen, nicht hardcoden. Tooltip-Background dito.
- Datei-Icons, Status-Icons: in SVG inline mit `currentColor`, nicht als PNG.

### 4.5 Lint-Regel (PR-C, kritisch — sonst kehrt das Problem bei jeder neuen Komponente wieder)

ESLint-Rule oder `stylelint`-Rule, die folgendes als Error markiert:

- Hex-Farben in `*.tsx`/`*.css` außerhalb von `tokens.css` und `tailwind.config.ts`
- Tailwind-Klassen `bg-white|black|gray-*|slate-*|zinc-*|neutral-*|stone-*` außerhalb einer explizit gewhitelisteten Liste
- `style={{ color|background|borderColor: ... }}` (nur in Charts erlaubt, dort über Token-Lookup)

Pre-commit-Hook + CI-Check. PR fällt durch wenn Verstöße existieren.

---

## 5. Komponenten-Audit & -Polish

### 5.1 Bestandsaufnahme (Teil von PR-A)

Liste **aller** UI-Komponenten in `components/ui/` + Inventar wo sie verwendet werden. Plus Liste der „Ad-hoc"-Komponenten, die in Page-Files inline definiert wurden (Anti-Pattern, sollten extrahiert werden).

### 5.2 Komponenten-Polish (PR-D)

Pro Komponente — Mini-Checkliste:

- [ ] Verwendet ausschließlich semantische Tokens
- [ ] Hat sinnvolle States: default, hover, active, focus-visible, disabled, loading
- [ ] Focus-Ring sichtbar (mindestens 2px `border-focus`, niemals `outline: none` ohne Ersatz)
- [ ] Hat ARIA-Label/Role wo nötig
- [ ] Funktioniert in Dark Mode
- [ ] Hat mindestens ein Storybook/Playground-Eintrag (falls Storybook im Stack, sonst `app/_design/` Route)

#### Konkrete Kandidaten zum Polishen (Priorität nach Sichtbarkeit):

1. **Buttons** — Variants konsolidieren: `primary`, `secondary`, `tertiary`/`ghost`, `danger`, `link`. Sizes: `sm`, `md`, `lg`. Icon-only-Variante mit Min-Hit-Area 44×44.
2. **Inputs** — Text, Number, Date, Select, Multi-Select, Hybrid-Select, Textarea, File-Upload. Einheitliche Label-Position (oben), Hilfetext darunter, Error-Style.
3. **Tables** — Sticky-Header, Zebra-Stripes optional, Hover-Row, Sortier-Indikator, Spalten-Resize wenn QAF/Workshop-Tabellen.
4. **Modals/Drawers** — Backdrop, Focus-Trap, Esc-to-close, Slide-from-right für Side-Drawers, zentriert für Modals.
5. **Cards** — Default, Elevated, Outlined. Hover-State sparsam (nur wenn klickbar).
6. **Toasts/Alerts** — `success`, `warning`, `danger`, `info`. Max. 3 gleichzeitig stapelbar, auto-dismiss 5s außer bei `danger`.
7. **Tabs** — Horizontal default, Vertical optional. Aktiver Tab mit `brand-primary`-Underline.
8. **Tooltips** — siehe Abschnitt 6.
9. **Empty States** — Jedes Listen-/Tabellen-View braucht einen sauberen Empty State (Illustration + Headline + CTA).
10. **Loading States** — Skeleton-Loader für Tabellen, Spinner für Buttons, Progress-Bars für Uploads.

### 5.3 Spacing- und Layout-Disziplin

- Page-Container: `max-w-7xl mx-auto px-6 py-8` als Standard, Ausnahmen begründen.
- Section-Spacing: `space-y-8` zwischen Hauptblöcken, `space-y-4` innerhalb.
- Form-Spacing: `space-y-6` zwischen Feldgruppen, `space-y-2` zwischen Label-Input-Hilfe.
- **Keine** willkürlichen `mt-7`, `pb-13` — nur Token-Werte des 4px-Grids.

---

## 6. Tooltip-System

### 6.1 Anforderung

Jedes nicht-selbsterklärende Feld bekommt Mouseover-Tooltip mit Erläuterungstext. Tooltips:

- Bilingual (DE/EN/ZH soweit verfügbar)
- Inhaltlich von Admin/Masteradmin editierbar
- Über alle Module hinweg zentral gepflegt (eine Tabelle `tooltips`)
- Performant (kein Performance-Hit bei Tabellen mit 50+ Zeilen mit Tooltips)

### 6.2 Datenmodell

```
tooltips
├── id
├── key          (z. B. "qaf.summary.material_kosten")
├── label_de     (kurzer Titel, optional)
├── label_en
├── label_zh
├── content_de   (Mouseover-Text)
├── content_en
├── content_zh
├── tenant_id    (NULL = global, sonst tenant-spezifischer Override)
└── updated_at
```

Komponente `<TooltipText tooltipKey="qaf.summary.material_kosten">Materialkosten</TooltipText>` rendert Label + Info-Icon, Hover öffnet Popover mit Content.

### 6.3 Implementierung

- **Radix UI Tooltip** (falls noch nicht im Stack: Radix kommt rein, ist Industrie-Standard, accessible by default).
- Lazy-Load der Tooltip-Inhalte: erst beim Hover-Trigger fetchen falls noch nicht im Client-State.
- Fallback wenn kein Eintrag existiert: nur Label rendern, ohne Info-Icon. Kein Crash, kein Konsolen-Error.
- Default-Set: alle bekannten Tooltips aus der BMW-Auftragsklärungs-Checkliste + QAF-Erläuterungen (aus den vorherigen Modulen) als Seed-Migration.

### 6.4 Admin-UI für Tooltip-Pflege

Sub-Tab in den globalen Einstellungen (für Admin/Masteradmin). Tabelle mit Suchfilter, Inline-Edit, Bulk-Export/Import als CSV (für Übersetzungs-Workflows).

---

## 7. „Nicht überfüllt" — konkrete Aufräum-Regeln

Subjektive Heuristik, aber harte Anwendungsregel:

- **Maximal 3 primäre Aktionen** sichtbar pro View. Alles weitere in Overflow-Menü (`...`).
- **Maximal 2 Spaltenebenen** in Tabellen-Headern. Tiefer geschachtelte Header (z. B. die QAF-3-Ebenen-Header aus dem Original-Excel) werden ABstrakter zusammengefasst.
- **Tabs vs. Side-Nav**: Mehr als 7 Tabs auf einer Ebene → in Side-Nav umstellen.
- **Whitespace > Linien**: Wenn Trennung zwischen Sections nötig, primär durch Whitespace, sekundär durch `--border-default`-Hairline. Keine fetten Borders, keine Boxes-in-Boxes.
- **Inline-Validierung sparsam**: Errors erst bei Blur, nicht bei jedem Keystroke. Ausnahme: Format-Checks (E-Mail, Datum).
- **Confirmations**: nur bei destruktiven Aktionen (Delete, Promote-mit-Daten-Override). Save-Buttons ohne Confirm.

---

## 8. A11y-Basics (nicht verhandelbar)

- **Kontrast WCAG AA**: Body-Text mindestens 4.5:1, UI-Elemente 3:1. Validieren mit Stark/Axe in CI.
- **Focus visible** überall — `outline: none` ist verboten ohne Ersatz-Focus-Ring.
- **Tastatur-Navigation** funktioniert in allen Modalen, Drawern, Menüs, Tabellen-Aktionen.
- **ARIA-Labels** auf Icon-Only-Buttons, Form-Inputs, Status-Indikatoren.
- **Reduced-Motion**: `@media (prefers-reduced-motion)` respektieren, Animationen abschalten.
- **Lang-Attribute** auf `<html lang="...">` setzen je nach gewählter UI-Sprache.

CI-Check via `@axe-core/playwright` auf zwei kritischen Pfaden (Login + Intake-Form + LSC-Workshop-Detail). Wenn Test fällt: PR fällt.

---

## 9. Lieferung in PRs

Jede Phase = eigener PR, in dieser Reihenfolge:

1. **PR-A: Inventur + CI-Doku-Lookup + Token-Foundation**
   - `/investigate`-Output (BMW-CI-Doku im Repo lokalisieren und lesen)
   - Codebase-Scan-Inventur (Abschnitt 4.1)
   - `app/globals.css` mit Token-CSS-Variablen (Light + Dark)
   - `tailwind.config.ts` referenziert Tokens
   - Keine Komponenten-Änderungen in diesem PR
2. **PR-B: BMW-Fonts lokal**
   - Font-Build-Pipeline (`scripts/fonts/build.mjs`)
   - 7 Quelldateien → woff2 + subsetted
   - `@font-face`-Regeln in `globals.css`
   - `tailwind.config.ts` Font-Family-Tokens
   - LICENSE.md im fonts-Ordner (mit Kais abklären, **nicht erfinden**)
   - Sichtprüfung: Login-Screen + Dashboard rendern in BMW-Schrift
3. **PR-C: Token-Migration**
   - Alle Funde aus Inventur ersetzt durch semantische Tokens
   - Dark Mode funktioniert flächendeckend
   - Theme-Toggle im User-Menü (mit `next-themes`)
   - SSR-Fest, kein FOUC
4. **PR-D: Lint-Regeln + CI-Checks**
   - ESLint/stylelint-Rules gegen hardcoded Farben
   - `@axe-core/playwright`-Tests auf 2 kritischen Pfaden
   - Pre-commit-Hook (lefthook oder husky)
   - PR-fall-through bei Verstößen aktiv
5. **PR-E: Komponenten-Polish (Buttons, Inputs, Cards)**
   - Variants, States, Focus, Loading, Empty
6. **PR-F: Komponenten-Polish (Tables, Modals, Drawers, Tabs)**
   - Insbesondere QAF/Workshop-Tabellen mit Condensed-Font
7. **PR-G: Tooltip-System**
   - Schema, Radix-Tooltip-Wrapper, Seed-Migration für Default-Tooltips, Admin-UI
8. **PR-H: Toasts, Alerts, Empty-States, Loading-States**
   - Konsistent über alle Module
9. **PR-I: Charts in Dark Mode + Brand-Farben**
   - Recharts (oder gewählte Chart-Lib) liest aus Tokens
   - Logo/Icon-Switching Light/Dark
10. **PR-J: Spacing- und Layout-Konsolidierung**
    - Page-Container, Section-Spacing, Form-Spacing standardisiert
    - Aufräum-Regeln aus Abschnitt 7 angewendet

**Pre-PR-Workflow** (jedes Mal):
- `/review` vor jedem PR
- `/qa` bei UI-PRs (E, F, G, H, I, J)
- `/cso` bei PR-G (RLS für `tooltips`-Tabelle), sonst nicht relevant

---

## 10. Was Claude liefern soll, BEVOR Code geschrieben wird

1. **`/investigate`-Output**:
   - BMW-CI-Doku gefunden? Wo? Auszug der relevanten Vorgaben.
   - Aktueller Stack: `next-themes` vorhanden? Radix? Tailwind-Version? CSS-Variablen-Konvention?
   - Inventur-Ergebnis aus Abschnitt 4.1 als Tabelle.
2. **Plan-Dokument** mit allen 10 PRs:
   - Pro PR: Dateien, geschätzter LOC-Diff, geschätzter Aufwand in Personentagen.
   - Risiko-Einschätzung pro PR (z. B. PR-C kann eine sehr große Diff werden — wie geht das in einen reviewbaren PR? Aufsplitten nach Modul?).
3. **Mindestens 6 offene Fragen** an Kais. Beispiele:
   - Italic-Schnitte: ja oder nein? Wenn nein, alle `<em>`/`italic` Klassen im Code prüfen + ggf. ersetzen durch Bold/Color-Highlight.
   - BMWGroupTN nur für Display oder auch für H4/H5? Mein Default: nur H1/H2/H3, sonst BMW Group Bold.
   - Eigener Mono-Font erwünscht oder System-Mono ok? (BMW hat keinen Mono-Font.)
   - Theme-Toggle nur Light/Dark/System oder auch „BMW-Blau Akzent" custom?
   - Tooltip-Default-Set: Welche Module sollen seed-Tooltips bekommen? Alle gleichzeitig oder schrittweise?
   - Charts in Dark Mode: BMW-Blau bleibt als Primärfarbe, oder gibt es eine extra Dark-Mode-Variante (heller Blau)?
4. **Reality-Check / Push-Back**: Was an diesem Brief ist Overkill, hat Konflikt mit Stack, oder ist falsch priorisiert? Mindestens 3 Punkte mit Begründung.
5. **Vorschlag für Sub-Set-Strategie der Fonts**: 7 wie hier empfohlen, oder andere Aufteilung? Mit Größenkalkulation.

Erst nach Kais' explizitem Go beginnt PR-A.

---

## 11. Bereits geklärt (NICHT erneut hinterfragen)

- BMW-Fonts werden **lokal gehostet**, kein CDN, kein Google-Fonts-Fallback im Production-Build.
- Subsetting auf Latin-Range ist Default (DE/EN/ZH-Latinwerte). ZH-Glyphen kommen aus System-Fonts oder werden in V2 separat behandelt (BMW hat keine CJK-Fonts in dem Set).
- Token-System ist die EINZIGE Quelle für Farben/Sizes — keine Ausnahmen für „nur dieses eine Mal".
- Dark Mode ist gleichberechtigt zu Light Mode, kein Afterthought.
- Tooltip-Inhalte sind editierbar durch Admin/Masteradmin, NICHT durch Fachbereich.
- A11y AA ist Pflicht. AAA ist nice-to-have, nicht Pflicht.
