# OVALE PERFORMANCE — Design System

> Outil interne B2B sobre, ton sérieux/pédagogique. Influences : Linear (densité), Cal.com (parcours), Notion (édition inline), Stripe (tableaux), AFNOR (palette).
> **Stack** : HTML5 + CSS variables + Alpine.js + Lucide SVG. Zéro build.

## 1. Tokens

Tous les tokens sont définis comme variables CSS dans `assets/tokens.css`. Toujours référencer par variable (`var(--op-primary)`), jamais par valeur en dur.

### Couleurs

| Rôle | Variable | Hex | Usage |
|---|---|---|---|
| Primary | `--op-primary` | `#1B4D3E` | Vert sapin OP — boutons primaires, liens, accents |
| Primary 700 | `--op-primary-700` | `#163E32` | Hover du primary |
| Primary 100 | `--op-primary-100` | `#E5EFEB` | Fonds de focus, badges discrets |
| Primary 50  | `--op-primary-50`  | `#F2F7F4` | Fonds de section actifs (hover ligne table) |
| Accent | `--op-accent` | `#D97706` | Orange ambré — **CTA principal uniquement** (Générer, Exporter) |
| Surface | `--op-bg` / `--op-bg-elevated` / `--op-bg-sunk` | `#FAFAF9` / `#FFFFFF` / `#F4F4F2` | Fond app / cartes / toolbars |
| Border | `--op-border` / `--op-border-strong` / `--op-border-subtle` | `#E7E5E4` / `#D6D3D1` / `#F1EFEC` | Standard / hover / interne |
| Text | `--op-text` / `--op-text-muted` / `--op-text-subtle` | `#1C1917` / `#57534E` / `#78716C` | Principal / secondaire / labels |

**Belts** (badges) : `--op-belt-yellow` `#EAB308`, `--op-belt-green` `#22C55E`, `--op-belt-black` `#171717` (avec leur `-bg` associé).

**States** : `--op-success` `#16A34A`, `--op-warning` `#B45309`, `--op-error` `#DC2626`, `--op-info` `#1D4ED8`.

### Typographie

- **Sans** : `Inter` (Bunny Fonts, RGPD-friendly) → `var(--op-font-sans)`
- **Mono** : `JetBrains Mono` (data, IDs, code) → `var(--op-font-mono)`
- Échelle : `--op-text-xs` 12 → `--op-text-4xl` 36px

### Espacement & radius

Échelle 4 → 64px. Radius : `--op-radius-sm` 4 / `--op-radius` 6 / `--op-radius-md` 8 / `--op-radius-lg` 12.

### Ombres

Très parcimonieuses. `--op-shadow-xs/sm/md/lg`. Jamais d'ombre forte sur fond clair.

## 2. Composants

### Header (`.op-header`)
Sticky, hauteur 56px. Brand mark + nav (Génération / Mes cas / Prompts / Stats) + utilisateur. Injecté via `window.opHeader('active-key')` depuis `app.js`.

### Boutons (`.op-btn`)
Variantes : `--primary` (vert), `--accent` (orange, CTA principal), `--secondary` (surface), `--ghost`, `--danger`. Tailles : `--sm`, default, `--lg`, `--xl`, `--block`, `--icon`.

```html
<button class="op-btn op-btn--accent op-btn--lg">Générer le cas</button>
```

### Inputs (`.op-input`, `.op-textarea`, `.op-select`)
Bordure `--op-border`, focus `--op-primary-500` + halo `--op-primary-100`. Labels via `.op-field__label` (font-weight 500).

### Badges (`.op-badge`)
Pilule uppercase 11px. Variantes : `--neutral`, `--yellow/green/black` (belts), `--success/warning/error/info`, `--draft`. Toujours utiliser `OPFmt.beltClass(beltId)` pour mapper.

### Cards (`.op-card`)
Surface élevée + bordure. Header (`.op-card__header`) optionnel.

### Belt cards (`.op-belt-card`)
Cartes cliquables de sélection (V1). Strip de couleur en haut, titre, description, méta (durée + coût + sections). État `is-selected` = bordure `primary` + halo.

### Tables (`.op-table`)
Dense, header uppercase 11px. Hover = `--op-primary-50`. Variante `.v3-ad-table` pour cellules éditables.

### Belts (utilitaires)
Toujours utiliser le mapping `OP_BELTS` (id → label, color, estTime, estCost, sections).

### Toast
`window.opToast('Cas sauvegardé')` — slide-in bas, auto-dismiss 2,2 s.

### Skeleton
Classe `.op-skeleton` — fond animé, durée 1,4 s.

## 3. Patterns

- **CTA orange ambré (`--accent`)** : 1 par écran maximum (Générer, Exporter, Télécharger les 3).
- **Vouvoiement** systématique. Pas de marketing. Vocabulaire NF X06-091 strict (Belt, VSM, Pareto, Cpk, MUDA…).
- **Icônes Lucide** uniquement, via `window.opIcon('name')`. Jamais d'emoji dans l'UI.
- **Status dots** (V6) : couleur sémantique + libellé court. `draft` (ambre), `final` (vert), `in_review` (bleu), `archived` (gris).
- **Édition inline (V3)** : `contenteditable="true"` + classe `.v3-edit`. Hover = fond `--op-bg-sunk`, focus = halo primary. Pas de mode "édition" séparé.
- **Sauvegarde auto** : debounce 1,5 s sur `markDirty()`, footer affiche état (`Sauvegardé il y a 3 s`).
- **Raccourcis clavier** (V3) : `⌘ S` save, `⌘ E` export, `Esc` ferme panel.

## 4. Couche data — fixtures

Tout est dans `assets/app.js` :

| Objet global | Contenu |
|---|---|
| `window.OP_API` | URL des webhooks n8n (à brancher en prod) |
| `window.OP_BELTS` | yellow/green/black + descriptions, durées, coûts |
| `window.OP_SECTORS` | 8 secteurs |
| `window.OP_CASE_DETAIL` | 1 cas YB ultra-détaillé pour V3/V4/V5 |
| `window.OP_CASES_LIST` | 12 entrées pour V6 |
| `window.OP_STATS` | KPIs + monthly_cost + top_sectors pour V8 |
| `window.OP_PROMPTS` | 3 prompts système pour V7 |
| `window.OPFmt` | Helpers FR : euro, pct, date, relative, duration, belt, beltClass |
| `window.OPIcons` / `window.opIcon(name)` | SVG inline Lucide |

**Branchement webhook** : remplacer chaque `setTimeout`-mock par un `fetch(OP_API.xxx)`. Le shape de réponse attendu est documenté dans le brief — voir `OP_CASE_DETAIL` pour le schéma cible.

## 5. Accessibilité

- Contraste WCAG AA validé (vert `#1B4D3E` sur `#FAFAF9` = 9,1:1).
- `aria-label` sur tous les boutons icônes (sans texte).
- Navigation clavier : Tab + Enter sur tous les contrôles, focus ring visible (`--op-primary-500`).
- Régions live (V2 streaming) : `aria-live="polite"`.

## 6. Anti-patterns à éviter

- ❌ Pas de gradient marketing
- ❌ Pas d'emoji dans l'UI (sauf icônes lucide)
- ❌ Pas de "AI ✨" ou "Magie en cours"
- ❌ Pas d'ombre portée sur les cartes (`--op-shadow-xs` au max)
- ❌ Pas de couleurs en hex en dur — toujours via variable
- ❌ Pas de mobile (desktop only, min 1280px)
