# Templates de Documentation SmartStack

> **Objectif:** Documentation lisible par un metier, pas seulement par des developpeurs.
> Chaque module doit repondre a: "Qu'est-ce que ca m'apporte ?"

---

## STRUCTURE OBLIGATOIRE (Type: user)

Chaque documentation de module DOIT inclure ces sections dans cet ordre:

| # | Section | Objectif | Public cible |
|---|---------|----------|--------------|
| 1 | **Objectif** | Ce que fait le module et a quoi il sert (description simple, redigee comme un objectif) | Decideurs, Managers |
| 2 | **Acces & roles** | Qui peut faire quoi (table Role × Actions × Portee, derivee de `report.accessRoles` — code + seed) + URL | Utilisateurs |
| 3 | **Apercu de l'interface** | **Mock UI annote** reproduisant la page reelle | Utilisateurs |
| 4 | **Formulaire de creation** | **Mock UI annote** du formulaire de creation | Utilisateurs |
| 5 | **Vue detail** | **Mock UI annote** de la vue detail/edition | Utilisateurs |
| 6 | **Cas d'usage** | Exemples concrets avec personas | Utilisateurs finaux |
| 7 | **Fonctionnalites** | Liste detaillee des capacites avec exemples | Utilisateurs |
| 8 | **FAQ** | Questions non-techniques | Tous |

| # (opt-in `--tech`) | Section | Objectif | Public cible |
|---|---------|----------|--------------|
| 9 | **Reference technique** | Endpoints API + regles metier (collapsible) | Admins |

> **Reference technique = OPT-IN, OFF par defaut.** La section 9 n'est generee
> qu'avec le flag `--tech`. Les permissions vivent desormais en Section 2 (vue
> par role) ; la section technique garde la vue par endpoint pour les admins.
> Un re-run SANS `--tech` ne supprime JAMAIS une section technique existante.

> **OBLIGATOIRE — Section 1 = Objectif :** toujours presente, en premier, comme
> **section numerotee a part entiere** (`t('sections.objective')` + corps `t('objective')`).
> Le `subtitle` du header ne la remplace PAS, meme s'il resume deja le module.
> L'omettre echoue le gate step-03 (`existingDoc.missingRequired` = `['objective']`).
>
> **ADAPTATION autorisee :** les sections "interface" du milieu (Apercu / Formulaire /
> Vue detail) s'adaptent a la forme reelle de la page — une page de reglages a onglets
> devient une section par onglet ; un Kanban devient un board. **Fixes** : Section 1
> Objectif, puis Acces & roles, et la queue (Cas d'usage, Fonctionnalites, FAQ —
> plus l'annexe Reference technique si `--tech`). On adapte le corps, on ne
> supprime jamais l'Objectif.
>
> **SUPPRIME:** Les sections "Benefices" et "Avant/Apres" ne sont plus incluses.
> Ce n'est pas un document de vente, mais une documentation utilisateur.

### Header OBLIGATOIRE (au-dessus de la Section 1) — structure fixe

Tout doc `user` ouvre par un header **identique** d'une page a l'autre (c'est l'ecart
le plus visible quand il manque). Dans cet ordre exact :

| Element | Rendu | Clé i18n |
|---------|-------|----------|
| Titre + icone | `<h1 class="text-3xl font-bold …">` | `title` |
| **Tagline accent** | `<p class="text-xl text-[var(--color-accent-600)] font-medium">` — **REQUISE** | `summary` |
| Sous-titre | `<p class="text-lg text-[var(--text-secondary)]">` | `subtitle` |
| Ligne de stats | `<div class="flex gap-4 mt-3 text-sm …">` — **inline, PAS de pills** | `header.{businessRules,apiEndpoints,…}` |

- **Tagline (`summary`) obligatoire** : une phrase accent qui resume l'usage. Son
  absence echoue le gate step-03 (`existingDoc.missingRequired` = `['summary']`).
  *(`valueProposition` est l'ancien nom de cette clé — migrer vers `summary`.)*
- **Stats = texte inline** : `<span>{N} {t('header.xxx')}</span>` — le compteur est
  un noeud texte frere. **NE PAS** utiliser `t('header.xxx', { count })` : si la
  valeur i18n n'a pas `{{count}}`, le nombre disparait (bug observe). **PAS de badges
  pills**, et pas de badge parasite (ex. le nom de l'app) dans la ligne de stats.
- **Stats conservees avec ou sans `--tech`** : `header.businessRules` ET
  `header.apiEndpoints` restent affichees meme quand la section 9 n'est pas
  generee — les comptes restent factuels (l'extraction ne change pas) et la
  coherence du header entre pages est une regle dure (SKILL.md regle 24).

---

## REGLE FONDAMENTALE: Lire la vraie page AVANT de generer

> **OBLIGATOIRE:** Avant de generer le Mock UI, lire le composant TSX de la vraie page
> (ex: `TenantsTemplatePage.tsx`, `UsersPage.tsx`). Le Mock UI doit reproduire
> fidelement l'interface reelle, PAS une interface generique avec des KPI/tables.
>
> - Si la vraie page affiche 2 cartes template → le Mock UI montre 2 cartes template
> - Si la vraie page est un tableau de gestion → le Mock UI montre un tableau
> - Si la vraie page est un Kanban → le Mock UI montre un Kanban

---

## REGLE STYLE : Discret, pas flashy, contexte DocPanel-aware

La documentation est consultee dans **deux contextes** :

| Contexte | Largeur | Contraintes |
|----------|---------|-------------|
| Page pleine | ~`max-w-5xl` (~1024px) | Marges horizontales `px-4` ou `px-6` |
| **DocPanel** (panneau lateral droit) | ~480px | Pas de debordement, padding compact, texte plus petit OK |

### Couleurs : utiliser UNIQUEMENT les variables CSS du theme

> **INTERDIT** : `bg-blue-50`, `bg-green-100`, `border-blue-200`, `text-blue-800`, ou toute couleur Tailwind hardcodee.
> **INTERDIT** : `bg-[var(--info-bg)]`, `bg-[var(--success-bg)]`, `bg-[var(--warning-bg)]`, `bg-[var(--error-bg)]` pour les blocs d'information / annotation / solution. Ces couleurs sont reservees aux **vraies alertes** (toasts, validation form, banderoles d'erreur), pas a la mise en valeur visuelle de paragraphes.
>
> **EXCEPTIONS legitimes** : les **badges semantiques courts** (HTTP method GET/POST/PUT/PATCH/DELETE, status pills "active/inactive", severity tags) PEUVENT utiliser les couleurs `--info/success/warning/error/accent-*`. Convention universelle. Critere : badge `<= 4em` de large, font `text-xs`, pas un container d'information narrative.

### Pattern d'accent recommande (Annotation, Solution, Tip, etc.)

```tsx
<div className="rounded-md border-l-2 border-[var(--color-accent-500)] bg-[var(--bg-secondary)] p-3">
  ...
</div>
```

- `bg-[var(--bg-secondary)]` : fond neutre (gris clair en light / gris fonce en dark)
- `border-l-2 border-[var(--color-accent-500)]` : **bordure gauche fine** comme indicateur visuel (PAS de bordure tout-autour)
- Padding `p-3` ou `px-3 py-2` selon densite voulue

### Card pattern OFFICIEL SmartStack

Le pattern **OBLIGATOIRE** pour toute card (sections principales, items de listes, blocs d'info) est celui utilisé par les vraies pages SmartStack (`UserDashboardPage.tsx`, `ProfilePage.tsx`, `MyTenantsPage.tsx`) :

```tsx
className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]"
```

| Token | Role | NE PAS remplacer par |
|-------|------|----------------------|
| `bg-[var(--bg-card)]` | Fond de card | `bg-white`, `bg-gray-50`, `bg-[var(--bg-secondary)]` |
| `border border-[var(--border-color)]` | Bordure subtile | `border-gray-200`, pas de border |
| `rounded-[var(--radius-card)]` | Radius (12px) | `rounded-lg` (= 8px ≠ card), `rounded-md` |
| `p-6` / `p-5` / `p-4` | Padding interne (densite) | px aleatoires |

> **NE PAS UTILISER** `className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]"` seul : la classe `.card` du theme n'inclut PAS de border (juste fond + shadow-sm). Les vraies pages SmartStack utilisent le pattern explicite avec **border** pour une separation plus nette dans les listes / grilles. **Adopter le pattern explicite pour rester aligne**.

**Variantes** :
- **Card highlighted** (selectionnee, focus) : ajouter `border-[var(--color-accent-500)] ring-2 ring-[var(--color-accent-500)]/20`
- **Card secondaire** (item interne, list row) : `bg-[var(--bg-secondary)] border border-[var(--border-color)] rounded-[var(--radius-card)] p-4`
- **Card cliquable** (button-like) : ajouter `hover:bg-[var(--bg-hover)] hover:border-[var(--color-accent-500)] transition-all`

### Couleurs SmartStack : mapping Tailwind → tokens

> **Délégué au CLI ui-polish.** N'applique PAS ce mapping à la main : après génération, `/ui-components` (CLI ui-polish, étape 03) convertit les couleurs Tailwind hardcodées en tokens de façon **déterministe**. La table ci-dessous est une **référence** (ce que le CLI applique) — ne la recopie pas dans le code généré.

Mapping de référence (appliqué par ui-polish) quand une source contient du Tailwind hardcode :

| Tailwind hardcode | Token SmartStack a utiliser | Contexte |
|-------------------|-----------------------------|----------|
| `bg-blue-50` / `bg-blue-100` | `bg-[var(--info-bg)]` | **Alerte info uniquement** (sinon `bg-[var(--bg-secondary)]`) |
| `text-blue-700` / `text-blue-800` | `text-[var(--info-text)]` ou `text-[var(--color-accent-700)]` | Info vs accent (marque) |
| `border-blue-200` / `border-blue-500/20` | `border-[var(--info-border)]` ou `border-[var(--color-accent-500)]/20` | — |
| `bg-green-50` / `bg-green-100` | `bg-[var(--success-bg)]` | **Vrai succes uniquement** |
| `text-green-600` / `text-green-700` / `text-green-800` | `text-[var(--success-text)]` | — |
| `border-green-200` | `border-[var(--success-border)]` | — |
| `bg-red-50` / `bg-red-100` | `bg-[var(--error-bg)]` | **Vraie erreur uniquement** |
| `text-red-600` / `text-red-700` | `text-[var(--error-text)]` | — |
| `bg-amber-*` / `bg-yellow-*` | `bg-[var(--warning-bg)]` | **Vrai warning** |
| `text-amber-700` / `text-yellow-700` | `text-[var(--warning-text)]` | — |
| `bg-purple-*` / `bg-violet-*` | `bg-[var(--accent-bg)]` ou `bg-[var(--color-accent-500)]/10` | Accent visuel |
| `text-purple-700` | `text-[var(--color-accent-700)]` | — |
| `bg-gray-*` / `bg-slate-*` / `bg-zinc-*` | `bg-[var(--bg-secondary)]` ou `bg-[var(--bg-tertiary)]` | — |
| `text-gray-500` / `text-gray-600` | `text-[var(--text-secondary)]` / `text-[var(--text-tertiary)]` | — |

> **Rappel** : `--info-bg`/`--success-bg`/`--warning-bg`/`--error-bg` ne s'utilisent **QUE** pour des **alertes reelles** (toast, form validation, banniere d'erreur, badge HTTP method). Pour mettre en valeur un paragraphe descriptif (Annotation, Tip, Solution), utiliser le pattern d'accent discret `bg-[var(--bg-secondary)] border-l-2 border-[var(--color-accent-500)]`.

---

### Marges et largeur — REGLE CRITIQUE

> **PROBLEME OBSERVE** : si la Mock UI utilise du padding interne (`px-4 pb-4`) mais que les `<Annotation>` sont placees **hors** du conteneur padded, les annotations dépassent à gauche. Et si les barres internes ont une largeur fixe (px), elles débordent à droite en mode DocPanel (~480px).

**Regle inviolable** : **toutes les Annotation d'une meme section sont placees DANS le meme conteneur que le Mock UI**, avec le **MEME** padding horizontal. JAMAIS d'Annotation orpheline en dehors du conteneur padded.

```tsx
{/* BON — Annotation DANS la card, même padding que le Mock UI */}
<section>
  <h2>...</h2>
  <div className="bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] overflow-hidden">
    <div className="p-4 space-y-4">
      {/* Mock UI ici */}
      <div className="border rounded-lg p-3">...graphique...</div>

      {/* Annotations DANS la même card, même padding */}
      <div className="space-y-1.5">
        <Annotation>{t('section.annotations.element1')}</Annotation>
        <Annotation>{t('section.annotations.element2')}</Annotation>
      </div>
    </div>
  </div>
</section>

{/* MAUVAIS — Annotation HORS du conteneur padded → déborde à gauche */}
<section>
  <div className="bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
    <div className="px-4 pb-4">{/* Mock UI avec padding */}</div>
  </div>
  <Annotation>...</Annotation> {/* ← Décalée à gauche par rapport au Mock UI ! */}
</section>
```

**Largeur des éléments internes** :
- Le conteneur racine est **TOUJOURS** `<div className="space-y-8 max-w-5xl mx-auto pb-12">` — **PAS** de `px-` au niveau racine. Le padding horizontal vient du parent (`DocsLayout` en page pleine, `DocPanel` en panneau).
- **Toute card contenant un Mock UI** doit avoir `overflow-hidden` pour empêcher les barres ou éléments larges de déborder.
- **JAMAIS de width fixe en px** sur les barres de graphes : utiliser `flex-1`, `min-w-0`, `w-full` ou `%`. Les flex children doivent avoir `min-w-0` pour autoriser le rétrécissement.
- Les `<section>` n'ajoutent **JAMAIS** de `mx-` ou `w-` qui forceraient une largeur. Elles s'etalent sur le conteneur.
- Les Annotations utilisent `space-y-1.5` (espacement reduit). La marge supérieure (`mt-3`) **ne s'applique que si l'Annotation suit directement un Mock UI dans le même padding**.
- Les `card p-6` ou `card p-4` selon la densite ; en DocPanel, preferer `p-4` quand possible.

### Mapping recharts → Mock UI (FIDELITE OBLIGATOIRE)

Le Mock UI doit reproduire le **TYPE** de graphique de la vraie page (pas seulement les données). Avant de coder la mock, identifier le composant recharts utilisé dans la vraie page :

| Vrai composant | Type recharts | Mock UI fidele |
|----------------|---------------|----------------|
| `BarChart` (vertical) | `<BarChart>` + `<Bar>` | Histogramme avec `<div>` empilés en colonnes (`flex items-end h-N`, barres avec `height: %`) |
| `BarChart` (horizontal, `layout="vertical"`) | `<BarChart layout="vertical">` | Liste de barres horizontales (`<div>` avec `width: %`) |
| `PieChart` / `Doughnut` | `<PieChart>` + `<Pie>` | SVG `<circle>` avec `stroke-dasharray` OU `conic-gradient` en CSS |
| `LineChart` | `<LineChart>` + `<Line>` | SVG `<polyline>` ou path |
| `AreaChart` | `<AreaChart>` + `<Area>` | SVG path avec fill |
| `RadialBarChart` | `<RadialBarChart>` | SVG circle avec stroke-dasharray |

> **REGLE** : si la vraie page utilise `BarChart` (vertical, sans `layout="vertical"`), la mock DOIT être un histogramme vertical — **JAMAIS** une liste de barres horizontales. Inversement, `BarChart layout="vertical"` (Recharts → barres horizontales) → mock en barres horizontales. Vérifier le `layout` du composant `BarChart` source.

**Detection** : grep `BarChart\|PieChart\|LineChart\|AreaChart\|RadialBarChart\|layout=` sur le composant chart source (souvent `/components/dashboard/*.tsx` ou `/components/charts/*.tsx`).

### Fidélité des DONNÉES : vraie métrique + unité (PAS de % abstrait)

> **REGLE CRITIQUE** : les valeurs d'un graphe mock reflètent la **vraie métrique** de la page documentée. Avant de coder, lire le composant chart source et relever :
> - le **DTO + `dataKey`** (ex. `WeeklyStatsDto.dayStats[].totalDuration`) → **quelle grandeur** est tracée ;
> - le **formatteur de valeur** (ex. `formatDuration` → `45 min`, `1h 10m`) → **l'unité** affichée ;
> - les **labels d'axe** (`yAxisLabel`, `XAxis dataKey`) et toute **`ReferenceLine`** (ex. moyenne) ;
> - la couleur de série réelle (ex. `getCategoricalColor(0)` = `--dataviz-cat-1`).
>
> La mock montre cette grandeur **dans sa vraie unité** (minutes/heures, nombre de sessions, €, %…) avec le bon label — **JAMAIS** des barres en `%` 0-100 abstrait quand la vraie métrique est une durée/un compte, et **JAMAIS** les valeurs d'exemple de ce fichier recopiées telles quelles.
>
> *Exemple vécu* : `WeeklyUsageChart` trace une **durée moyenne de session/jour** + une ligne de moyenne. Une doc qui affiche « Lun 42 %, Mar 78 %… » a recopié l'exemple : il faut des durées (« 42 min, 1h18… ») et la ligne de moyenne.

### Couleurs des graphiques & KPI dashboard : palette `--dataviz-*`

> **REGLE** : tout Mock UI reproduisant une **page dashboard** (KPI cards, graphiques, légendes) colore ses **séries de données** avec la palette **`--dataviz-*`** — **jamais** `--color-primary-*` ni `--color-accent-*`. C'est la palette dédiée aux dashboards (colorblind-safe, surchargée à l'exécution par `ThemeContext.applyDataViz()`), exactement comme la vraie `DashboardPage.tsx`.

| Élément du dashboard | Token |
|----------------------|-------|
| Série unique (histogramme 1 métrique, ligne) | `var(--dataviz-cat-1)` (= `getCategoricalColor(0)` du vrai chart) |
| Catégories multiples (barres groupées, donut, légende) | `var(--dataviz-cat-1)` … `var(--dataviz-cat-12)` (cycler) |
| Tendance ↑ / ↓ / = | `var(--dataviz-trend-up)` / `var(--dataviz-trend-down)` / `var(--dataviz-trend-neutral)` |
| Dégradé séquentiel (heatmap, intensité) | `var(--dataviz-seq-low)` → `var(--dataviz-seq-mid)` → `var(--dataviz-seq-high)` |

> La card KPI reste une card standard (`bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]`) ; seuls le **chiffre**, la **barre**, le **segment** ou le **badge de tendance** portent une couleur `--dataviz-*` (via `style={{ backgroundColor: 'var(--dataviz-cat-N)' }}` ou `bg-[var(--dataviz-accent)]`).

**Histogramme vertical type** :
```tsx
{/* Hauteur MINIMALE h-24 (96px) en page pleine, h-20 (80px) en DocPanel.
    JAMAIS h-16 ou moins — les variations deviennent invisibles. */}
<div className="flex items-end gap-1 h-24 w-full">
  {data.map((d) => (
    <div key={d.label} className="flex-1 min-w-0 flex flex-col items-center gap-1">
      <div
        className="w-full rounded-t bg-[var(--dataviz-cat-1)]"
        style={{ height: `${(d.value / max) * 100}%`, minHeight: d.value > 0 ? '6px' : '0' }}
        title={formatValue(d.value)}  /* vraie unité de la page : "45 min", "1h 10m", "12 sessions"… */
      />
      <span className="text-[10px] text-[var(--text-tertiary)] truncate w-full text-center">{d.label}</span>
    </div>
  ))}
</div>
{/* `--dataviz-cat-1` = getCategoricalColor(0) du vrai chart. `d.value` = la VRAIE métrique
    (durée, nombre…), PAS un % abstrait. Si le vrai chart a une ReferenceLine (ex. moyenne),
    la reproduire : trait pointillé --dataviz-trend-neutral + label "Moyenne: 1h 02". */}
```

**Donut chart type (SVG)** :
```tsx
<svg viewBox="0 0 36 36" className="w-32 h-32 mx-auto">
  {segments.map((s, i) => (
    <circle
      key={i}
      cx="18" cy="18" r="15.9"
      fill="transparent"
      stroke={s.color}
      strokeWidth="3.5"
      strokeDasharray={`${s.pct} ${100 - s.pct}`}
      strokeDashoffset={s.offset}
      transform="rotate(-90 18 18)"
    />
  ))}
</svg>
```

### Donnees Mock EXPRESSIVES (anti-graphes-plats)

> **PROBLEME** : si les valeurs mock sont uniformes (ex: `[10, 12, 11, 13, 10]`), toutes les barres apparaissent de la meme taille → graphe **plat** illisible. La doc doit montrer **une histoire visuelle** claire.

**Regles de generation des mock data pour les graphes** :

| Critere | Regle |
|---------|-------|
| Ratio max/min (valeurs > 0) | **≥ 5x** entre la valeur la plus haute et la plus basse non-nulle |
| Pic clairement identifie | Au moins **1 valeur ≥ 90%** du max (pour qu'on voie un sommet) |
| Vallee identifiee | Au moins **1 valeur ≤ 15%** du max (pour qu'on voie un creux) |
| Zero plausible | Mettre `0` uniquement si realiste (ex: week-end pour une app pro, heure de nuit pour une app de jour) |
| Hauteur container | `h-24` (96px) page pleine, `h-20` (80px) DocPanel — **JAMAIS** `h-16` ou moins |
| `minHeight` barre non-nulle | `'6px'` minimum pour que les petites valeurs restent visibles |

**Exemples concrets — ILLUSTRATIFS, à RÉGÉNÉRER par module (NE PAS recopier ces valeurs)** :

> Ces blocs montrent la **forme** attendue (métrique réelle + unité + courbe expressive). Les chiffres sont des exemples : chaque doc produit les siens, tirés de la vraie page. Recopier `mockWeeklyData` / `mockHourlyData` tel quel = doc générique non conforme.

```tsx
// ✓ Hebdomadaire — VRAIE métrique de la page (ici: durée moyenne/jour, en minutes), PAS un % abstrait.
//   Rendue via le formatteur de la vraie page (ex. formatDuration → "1h 40", "42 min").
const mockWeeklyData = [
  { day: 'Lun', value: 42 },  // 42 min   → barre moyenne
  { day: 'Mar', value: 78 },  // 1h18     → barre haute
  { day: 'Mer', value: 61 },  // 1h01     → barre moyenne-haute
  { day: 'Jeu', value: 100 }, // 1h40     → PIC (le sommet)
  { day: 'Ven', value: 52 },  // 52 min   → barre moyenne
  { day: 'Sam', value: 12 },  // 12 min   → CREUX (week-end)
  { day: 'Dim', value: 0  },  // inactif  → vide assumé
];
// ratio max/min(>0) = 100/12 = 8.3x ✓  (le ratio porte sur la forme, l'affichage garde l'unité)

// ✓ Horaire — nombre de sessions par tranche : pic matin, creux midi, second pic apres-midi
const mockHourlyData = [4, 8, 18, 28, 45, 38, 22, 15, 32, 40, 28, 18, 10, 5];
// max=45, min(>0)=4 → ratio 11x ✓ ; double-bosse expressive

// ✓ Donut applications — répartition en % (le donut EST une métrique en %) : 1 dominant, 1 moyen, 1 minoritaire (somme = 100)
const mockAppSegments = [
  { label: 'Administration', pct: 65, color: 'var(--dataviz-cat-1)' },
  { label: 'Support',        pct: 25, color: 'var(--dataviz-cat-2)' },
  { label: 'Mon Espace',     pct: 10, color: 'var(--dataviz-cat-3)' },
];

// ✗ MAUVAIS — toutes les valeurs proches → graphe plat
const badWeekly = [{ day: 'Lun', value: 22 }, { day: 'Mar', value: 25 }, ...];
// ratio 25/22 = 1.13x → on voit pas la difference
```

> **Pattern recommande** : courbe en cloche, double-bosse (matin+apres-midi), ou pic-isole-week-end. Eviter une distribution lineaire ou uniforme.

---

## Mock UI TSX Approach (TYPE: user — PRIMARY)

> **Principe:** Pour les docs de type `user`, generer un **composant TSX autonome** (~400-600 lignes)
> avec des Mock UI **annotes** qui reproduisent fidelement les vraies pages
> de l'application. Chaque Mock UI est suivi d'annotations explicatives.
>
> **IMPORTANT:** NE PAS utiliser DocRenderer pour le type `user`. Generer un TSX standalone.

### Fichiers a generer par module

**1. Page TSX** (`index.tsx`, ~400-600 lignes) : Composant autonome avec Mock UI annotes.

**2. i18n dans les 4 langues** : FR (source), EN, DE, IT. Tous les fichiers sont generes.

### i18n JSON : Structure PLATE (pas de cle racine)

> **IMPORTANT:** Pour les pages standalone (type `user`), le JSON i18n doit etre **PLAT** (pas de cle racine).
> `useTranslation('docsAdministrationTenantsTemplate')` + `t('title')` resout en
> `docsAdministrationTenantsTemplate.title`. Si le JSON a une cle racine, ca devient
> `docsAdministrationTenantsTemplate.docsAdministrationTenantsTemplate.title` → ERREUR.

```json
// CORRECT pour standalone (type user) - JSON PLAT :
{
  "title": "Templates de Tenant",
  "subtitle": "Description...",
  "sections": { "businessValue": "Pourquoi ce module ?" }
}

// INCORRECT pour standalone :
{
  "docsAdministrationTenantsTemplate": {
    "title": "Templates de Tenant"
  }
}

// CORRECT pour DocRenderer (developer/database/testing) — NESTED sous la cle du namespace :
// DocRenderer resout t('<namespace>.overview.objective'), donc le fichier imbrique sous le namespace.
// (scaffold-doc fait cette imbrication automatiquement — tu fournis du contenu PLAT.)
{
  "docsAdministrationUsers": {
    "overview": { "objective": "..." }
  }
}
```

---

## DocRenderer Approach (TYPE: developer|database|testing)

> **Usage:** Pour les docs `developer`, `database`, et `testing` uniquement.
> Genere un fichier de DONNEES (`doc-data.ts`, ~50 lignes) + un wrapper minimal.
> Le rendu est assure par le composant partage `DocRenderer` dans `web/.../components/docs/`.
> Voir [data-schema.md](data-schema.md) pour le mapping complet.
>
> **⛔ SOURCE MONOREPO ONLY** — `DocRenderer` n'est PAS exporté par le package
> `@atlashub/smartstack` : sur un projet CLIENT (`frontendMode: client`),
> seuls les docs `user` sont possibles (scaffold-doc refuse les autres types).

### Fichiers a generer par module

**1. Data file** (`doc-data.ts`) : Voir [data-schema.md](data-schema.md) pour le mapping complet.

**2. Page wrapper** (`index.tsx`, ~10 lignes) :
```tsx
import { DocRenderer } from '@/components/docs';
import { docData } from './doc-data';

export default function {ModuleName}DocPage() {
  return <DocRenderer data={docData} backPath="/docs/business/{app}" />;
}
```

---

## Composant Helper: Annotation

> **OBLIGATOIRE** pour chaque section Mock UI. Un Mock sans explication ne sert a rien.
>
> **Design discret (NON flashy)** : fond neutre du theme + bordure gauche fine d'accent.
> N'utilise PAS `--info-bg`, `--success-bg`, `--warning-bg`, ni `bg-blue-*`/`bg-green-*` (Tailwind hardcode).
> Le composant doit rester lisible aussi bien en pleine page que dans le panneau `DocPanel`
> (largeur reduite ~480px). Pas de fond colore vif.

```tsx
import { Info } from 'lucide-react';
import type { ReactNode } from 'react';

function Annotation({ children }: { children: ReactNode }) {
  return (
    <div className="flex items-start gap-2 px-3 py-2 rounded-md border-l-2 border-[var(--color-accent-500)] bg-[var(--bg-secondary)] text-xs text-[var(--text-secondary)]">
      <Info className="w-3.5 h-3.5 mt-0.5 flex-shrink-0 text-[var(--text-tertiary)]" />
      <span className="leading-relaxed">{children}</span>
    </div>
  );
}
```

> **API** : le composant accepte `children` (pas `text`) pour pouvoir injecter du JSX riche (badges, `<code>`, etc.) au besoin. En usage standard, `children` est juste un appel `t('...')`.

**Pourquoi ce design :**
- `bg-[var(--bg-secondary)]` : fond neutre cohérent avec le reste de l'app (gris clair / gris foncé selon le mode)
- `border-l-2 border-[var(--color-accent-500)]` : bordure fine **uniquement à gauche** comme accent visuel (pas une boîte encadrée flashy)
- `text-[var(--text-secondary)]` + `text-xs` : texte discret, hierarchie claire avec le contenu principal
- `px-3 py-2` : padding compact qui respecte les marges du conteneur parent (essentiel pour DocPanel)

**Utilisation:** Apres chaque bloc Mock UI, ajouter un `<div className="space-y-1.5 mt-3">` contenant
les `<Annotation text={t('section.annotations.key')} />` pour chaque element visuel.

> **NE PAS** ajouter `mx-` ou `w-` sur les Annotations : elles doivent suivre la largeur du parent (section) pour eviter les debordements en mode DocPanel.

### Exemple d'annotation apres un Mock UI

```tsx
{/* Mock UI card ici */}
<div className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">...</div>

{/* Annotations explicatives — espacement reduit + marge superieure modeste */}
<div className="space-y-1.5 mt-3">
  <Annotation>{t('interface.annotations.icon')}</Annotation>
  <Annotation>{t('interface.annotations.title')}</Annotation>
  <Annotation>{t('interface.annotations.code')}</Annotation>
  <Annotation>{t('interface.annotations.defaultBadge')}</Annotation>
  <Annotation>{t('interface.annotations.cta')}</Annotation>
</div>
```

---

## Template TSX Mock UI (TYPE: user)

> **Chemin + namespace selon le mode** (extract-doc `resolved.frontendMode`) :
> - `source` : `web/smartstack-web/src/pages/docs/business/{platform|personal}/{application}/{module}/index.tsx`,
>   `useTranslation('docs{App}{Module}')` (camelCase).
> - `client` : `web/{projet}-web/src/pages/docs/business/{application}/{module}/index.tsx`
>   (PAS de segment `platform|personal`), `useTranslation('docs-{application}-{module}')`
>   (namespace KEBAB = nom du fichier i18n — contrat moduleResources).

```tsx
// {web}/src/pages/docs/business/[{platform|personal}/]{application}/{module}/index.tsx

import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
  ArrowLeft,
  Info,
  Users,
  Zap,
  ChevronDown,
  // + icones specifiques au module
} from 'lucide-react';

// ═══════════════════════════════════════════════════
// Helper: Annotation (obligatoire pour chaque Mock UI)
// Design discret : fond neutre + bordure gauche d'accent (PAS de fond colore vif).
// ═══════════════════════════════════════════════════
function Annotation({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex items-start gap-2 px-3 py-2 rounded-md border-l-2 border-[var(--color-accent-500)] bg-[var(--bg-secondary)] text-xs text-[var(--text-secondary)]">
      <Info className="w-3.5 h-3.5 mt-0.5 flex-shrink-0 text-[var(--text-tertiary)]" />
      <span className="leading-relaxed">{children}</span>
    </div>
  );
}

// ═══════════════════════════════════════════════════
// Data: API Endpoints (extraits du vrai controller)
// ═══════════════════════════════════════════════════
const apiEndpoints = [
  // Remplir avec les vrais endpoints du controller
  { method: 'GET', path: '/api/...', handler: 'GetAll', permission: '...' },
];

// ═══════════════════════════════════════════════════
// Data: Business Rules (extraites du domain/tests)
// ═══════════════════════════════════════════════════
const businessRules = [
  // Remplir avec les vraies regles metier
  { id: 'BR-001', rule: '...' },
];

// ═══════════════════════════════════════════════════
// Data: Acces & roles (report.accessRoles — UNIQUEMENT
// les lignes rapportees, ne JAMAIS inventer un droit)
// ═══════════════════════════════════════════════════
// Une entree par ligne de report.accessRoles.rows ; `key` est un identifiant
// camelCase stable derive du role (il indexe access.roles.<key>.* en i18n).
const accessRoles = [
  { key: 'admin' },
  { key: 'commercial' },
];

export default function {Module}DocPage() {
  const { t } = useTranslation('docsAdministration{Module}');

  return (
    <div className="space-y-8 max-w-5xl mx-auto pb-12">
      {/* BREADCRUMB + BACK */}
      <div>
        <Link to="/docs/business/administration/tenants"
          className="inline-flex items-center gap-1.5 text-sm text-[var(--text-secondary)] hover:text-[var(--color-accent-600)] mb-4">
          <ArrowLeft className="w-4 h-4" />
          {t('nav.backToParent')}
        </Link>
      </div>

      {/* HEADER — OBLIGATOIRE, structure fixe (voir "Header obligatoire" plus haut) */}
      <div>
        <h1 className="text-3xl font-bold mb-4 flex items-center gap-3">
          <ModuleIcon className="w-8 h-8 text-[var(--color-accent-600)]" />
          {t('title')}
        </h1>
        {/* Tagline accent — REQUISE (clé `summary`). Le gate step-03 bloque si absente. */}
        <p className="text-xl text-[var(--color-accent-600)] font-medium mb-2">
          {t('summary')}
        </p>
        <p className="text-lg text-[var(--text-secondary)]">
          {t('subtitle')}
        </p>
        {/* Ligne de stats — texte inline, PAS de pills. Le compteur est un noeud
            texte frere (`{N} {t('header.xxx')}`), JAMAIS via la pluralisation i18n
            `t('header.xxx', { count })` (si la valeur n'a pas `{{count}}`, le nombre
            disparait silencieusement). 2-3 stats : regles, endpoints + 1 metier. */}
        <div className="flex flex-wrap gap-4 mt-3 text-sm text-[var(--text-secondary)]">
          <span>{businessRules.length} {t('header.businessRules')}</span>
          <span>{apiEndpoints.length} {t('header.apiEndpoints')}</span>
          {/* + 1 stat specifique au module si pertinent (statuts, onglets, types…) */}
        </div>
      </div>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 1: OBJECTIF — Ce que fait le module / a quoi il sert       */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* Rediger {t('objective')} COMME UN OBJECTIF : une description       */}
      {/* simple et factuelle de ce que permet le module (ce qu'il fait, a   */}
      {/* quoi il sert), du point de vue utilisateur. PAS de probleme +      */}
      {/* solution, PAS d'argumentaire de vente.                             */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="objectif" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">1</span>
          {t('sections.objective')}
        </h2>

        <div className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
          <p className="text-[var(--text-secondary)] leading-relaxed">{t('objective')}</p>
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 2: ACCES & ROLES — qui peut faire quoi                    */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* Table Role | Peut faire | Portee construite depuis                 */}
      {/* report.accessRoles.rows — UNIQUEMENT les lignes rapportees (le     */}
      {/* code fait foi ; les warnings du join sont reportes a l'utilisateur */}
      {/* par le CLI, JAMAIS publies ici). Pas de breadcrumb : le doc        */}
      {/* s'ouvre en panneau sur la page elle-meme — seule l'URL reste.      */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="acces" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">2</span>
          {t('sections.access')}
        </h2>
        <div className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] space-y-4">
          <code className="block text-xs text-[var(--text-secondary)] break-all">
            /administration/{module}
          </code>
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="bg-[var(--bg-secondary)]">
                  <th className="text-left py-2 px-3 font-semibold">{t('access.columns.role')}</th>
                  <th className="text-left py-2 px-3 font-semibold">{t('access.columns.actions')}</th>
                  <th className="text-left py-2 px-3 font-semibold">{t('access.columns.scope')}</th>
                </tr>
              </thead>
              <tbody>
                {accessRoles.map((r) => (
                  <tr key={r.key} className="border-b border-[var(--border-color)] last:border-0">
                    <td className="py-2 px-3 font-medium">{t(`access.roles.${r.key}.name`)}</td>
                    <td className="py-2 px-3">{t(`access.roles.${r.key}.actions`)}</td>
                    <td className="py-2 px-3 text-[var(--text-secondary)]">{t(`access.roles.${r.key}.scope`)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          {/* Si report.accessRoles.source === 'ba' (rbac.md seul, non verifie
              contre le seed), afficher OBLIGATOIREMENT le caveat : */}
          {/* <Annotation>{t('access.unverified')}</Annotation> */}
          {/* Si report.accessRoles.unmappedCodePermissions est non vide,
              signaler ces permissions en caveat (portees par un endpoint mais
              accordees a aucun role) — jamais les passer sous silence. */}
        </div>
      </section>
      {/* FALLBACK (report.accessRoles.source === 'none' — projet sans state
          ni BA) : garder la carte simple historique — l'URL ci-dessus + une
          liste "permissions requises" depuis apiEndpoints[].permission
          (`<code className="break-all">{ep.permission}</code>` par action).
          Le gate n'exige alors PAS la table. */}

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 3: APERCU DE L'INTERFACE — Mock UI ANNOTE                 */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      {/*                                                                    */}
      {/* IMPORTANT: Le Mock UI ci-dessous doit reproduire fidelement       */}
      {/* la VRAIE page TSX du module. Lire le composant source d'abord.    */}
      {/*                                                                    */}
      {/* Exemples:                                                          */}
      {/*   - TenantsTemplatePage → 2 cartes template (Personal + Business)  */}
      {/*   - UsersPage → tableau de gestion avec filtres                    */}
      {/*   - TicketsPage → Kanban board avec colonnes par statut            */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="interface" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">3</span>
          {t('sections.interface')}
        </h2>

        {/* Mock UI reproduisant la vraie page */}
        <div className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
          {/* ... MOCK UI FIDELE A LA VRAIE PAGE ... */}
        </div>

        {/* ANNOTATIONS OBLIGATOIRES apres le Mock UI */}
        <div className="space-y-1.5 mt-3">
          <Annotation>{t('interface.annotations.element1')}</Annotation>
          <Annotation>{t('interface.annotations.element2')}</Annotation>
          {/* ... une annotation par element visuel important ... */}
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 4: FORMULAIRE DE CREATION — Mock UI ANNOTE                */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="form" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">4</span>
          {t('sections.createForm')}
        </h2>

        {/* Paragraphe d'introduction */}
        <p className="text-[var(--text-secondary)] mb-4">{t('form.desc')}</p>

        {/* Mock UI du formulaire */}
        <div className="border border-[var(--border-color)] rounded-lg p-6 bg-[var(--bg-secondary)]">
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
            {/* Champs de formulaire adaptes au module */}
          </div>
          <div className="flex items-center gap-3 mt-6 pt-6 border-t border-[var(--border-color)]">
            <button className="px-4 py-2 bg-[var(--color-accent-600)] text-white rounded-lg">
              {t('form.create')}
            </button>
            <button className="px-4 py-2 border border-[var(--border-color)] rounded-lg">
              {t('form.cancel')}
            </button>
          </div>
        </div>

        {/* ANNOTATIONS OBLIGATOIRES apres le formulaire */}
        <div className="space-y-1.5 mt-3">
          <Annotation>{t('form.annotations.field1')}</Annotation>
          <Annotation>{t('form.annotations.field2')}</Annotation>
          <Annotation>{t('form.annotations.actions')}</Annotation>
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 5: VUE DETAIL — Mock UI ANNOTE                            */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="detail" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">5</span>
          {t('sections.detailView')}
        </h2>

        {/* Paragraphe d'introduction */}
        <p className="text-[var(--text-secondary)] mb-4">{t('detail.desc')}</p>

        {/* Mock UI de la vue detail */}
        <div className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
          {/* ... MOCK UI FIDELE A LA VRAIE VUE DETAIL ... */}
        </div>

        {/* ANNOTATIONS OBLIGATOIRES */}
        <div className="space-y-1.5 mt-3">
          <Annotation>{t('detail.annotations.header')}</Annotation>
          <Annotation>{t('detail.annotations.tabs')}</Annotation>
          <Annotation>{t('detail.annotations.stats')}</Annotation>
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 6: CAS D'USAGE - Exemples concrets                        */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="cas-usage" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">6</span>
          {t('sections.useCases')}
        </h2>

        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
          {[1, 2, 3].map((i) => (
            <div key={i} className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
              <div className="flex items-center gap-3 mb-4">
                <div className="w-10 h-10 rounded-full bg-[var(--accent-bg)] flex items-center justify-center">
                  <Users className="w-5 h-5 text-[var(--accent-text)]" />
                </div>
                <div>
                  <div className="font-semibold">{t(`useCases.${i}.persona`)}</div>
                  <div className="text-sm text-[var(--text-secondary)]">{t(`useCases.${i}.role`)}</div>
                </div>
              </div>
              <div className="space-y-3">
                <div className="text-sm">
                  <span className="font-medium text-[var(--text-secondary)]">Situation:</span>
                  <p>{t(`useCases.${i}.situation`)}</p>
                </div>
                {/* Bloc "Avec la fonctionnalite" discret — accent gauche, pas de fond vif */}
                <div className="text-sm p-3 rounded-md border-l-2 border-[var(--color-accent-500)] bg-[var(--bg-secondary)]">
                  <span className="font-medium text-[var(--text-primary)]">Avec la fonctionnalite:</span>
                  <p className="text-[var(--text-secondary)]">{t(`useCases.${i}.withFeature`)}</p>
                </div>
              </div>
            </div>
          ))}
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 7: FONCTIONNALITES DETAILLEES                             */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="fonctionnalites" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">7</span>
          {t('sections.features')}
        </h2>

        <div className="space-y-4">
          {[1, 2, 3, 4, 5].map((i) => (
            <div key={i} className="p-6 bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
              <div className="flex items-start gap-4">
                <div className="w-10 h-10 rounded-lg bg-[var(--accent-bg)] flex items-center justify-center flex-shrink-0">
                  <Zap className="w-5 h-5 text-[var(--accent-text)]" />
                </div>
                <div className="flex-1">
                  <h3 className="font-semibold mb-2">{t(`features.${i}.title`)}</h3>
                  <p className="text-[var(--text-secondary)] mb-3">{t(`features.${i}.description`)}</p>
                  <div className="p-3 rounded-lg bg-[var(--bg-secondary)] text-sm">
                    <span className="font-medium text-[var(--color-accent-600)]">Exemple: </span>
                    {t(`features.${i}.example`)}
                  </div>
                </div>
              </div>
            </div>
          ))}
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* SECTION 8: FAQ                                                    */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="faq" className="scroll-mt-4">
        <h2 className="text-2xl font-bold mb-4 flex items-center gap-2">
          <span className="w-8 h-8 rounded-full bg-[var(--color-accent-600)] text-white flex items-center justify-center text-sm">8</span>
          {t('sections.faq')}
        </h2>

        <div className="space-y-3">
          {[1, 2, 3, 4, 5].map((i) => (
            <details key={i} className="bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)] group">
              <summary className="p-4 cursor-pointer hover:bg-[var(--bg-secondary)] rounded-[var(--radius-card)] font-medium flex items-center justify-between">
                {t(`faq.${i}.question`)}
                <ChevronDown className="w-4 h-4 text-[var(--text-secondary)] group-open:rotate-180 transition-transform" />
              </summary>
              <div className="px-4 pb-4 text-sm text-[var(--text-secondary)]">
                {t(`faq.${i}.answer`)}
              </div>
            </details>
          ))}
        </div>
      </section>

      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* ANNEXE OPT-IN (--tech) — SECTION 9: REFERENCE TECHNIQUE           */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      {/* OFF PAR DEFAUT : cette section n'est generee qu'avec le flag       */}
      {/* --tech. Les permissions par ROLE vivent en Section 2 ; celle-ci    */}
      {/* garde la vue par ENDPOINT (colonne Permission conservee — autre    */}
      {/* public : les admins). Un re-run SANS --tech ne supprime JAMAIS     */}
      {/* une section technique existante.                                   */}
      {/* ══════════════════════════════════════════════════════════════════ */}
      <section id="reference" className="scroll-mt-4">
        <details className="bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]">
          <summary className="p-4 cursor-pointer hover:bg-[var(--bg-secondary)] rounded-[var(--radius-card)] font-semibold flex items-center gap-2">
            <span className="w-8 h-8 rounded-full bg-[var(--bg-tertiary)] text-[var(--text-secondary)] flex items-center justify-center text-sm">9</span>
            {t('sections.technicalRef')}
          </summary>
          <div className="p-6 pt-2 space-y-8">

            {/* Regles metier */}
            <div>
              <h3 className="font-semibold mb-3">{t('techRef.businessRules')}</h3>
              <div className="overflow-x-auto">
                <table className="w-full text-sm">
                  <thead>
                    <tr className="bg-[var(--bg-secondary)]">
                      <th className="text-left py-2 px-3 w-24">ID</th>
                      <th className="text-left py-2 px-3">Regle</th>
                    </tr>
                  </thead>
                  <tbody>
                    {businessRules.map((rule) => (
                      <tr key={rule.id} className="border-b border-[var(--border-color)]">
                        <td className="py-2 px-3 font-mono text-xs">{rule.id}</td>
                        <td className="py-2 px-3">{t(`techRef.rules.${rule.id}`)}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>

            {/* API Endpoints */}
            <div>
              <h3 className="font-semibold mb-3">{t('techRef.apiEndpoints')}</h3>
              <div className="overflow-x-auto">
                <table className="w-full text-sm">
                  <thead>
                    <tr className="bg-[var(--bg-secondary)]">
                      <th className="text-left py-2 px-3">Method</th>
                      <th className="text-left py-2 px-3">Endpoint</th>
                      <th className="text-left py-2 px-3">Handler</th>
                      <th className="text-left py-2 px-3">Permission</th>
                    </tr>
                  </thead>
                  <tbody>
                    {apiEndpoints.map((ep, i) => (
                      <tr key={i} className="border-b border-[var(--border-color)]">
                        <td className="py-2 px-3">
                          <span className={`px-2 py-0.5 rounded text-xs font-medium border ${
                            ep.method === 'GET'    ? 'bg-[var(--info-bg)] text-[var(--info-text)] border-[var(--info-border)]' :
                            ep.method === 'POST'   ? 'bg-[var(--success-bg)] text-[var(--success-text)] border-[var(--success-border)]' :
                            ep.method === 'PUT'    ? 'bg-[var(--warning-bg)] text-[var(--warning-text)] border-[var(--warning-border)]' :
                            ep.method === 'PATCH'  ? 'bg-[var(--accent-bg)] text-[var(--accent-text)] border-[var(--accent-border)]' :
                                                     'bg-[var(--error-bg)] text-[var(--error-text)] border-[var(--error-border)]'
                          }`}>{ep.method}</span>
                        </td>
                        <td className="py-2 px-3 font-mono text-xs">{ep.path}</td>
                        <td className="py-2 px-3 text-[var(--text-secondary)]">{ep.handler}</td>
                        <td className="py-2 px-3 font-mono text-xs">{ep.permission}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>

          </div>
        </details>
      </section>
    </div>
  );
}
```

---

## Structure i18n (4 langues obligatoires)

> **IMPORTANT:** Generer les 4 langues: FR (source), EN, DE, IT.
> Chaque fichier i18n est au format **PLAT** (pas de cle racine) pour les pages standalone.

### Fichier: `docs-administration-{module}.json`

```json
{
  "nav": {
    "backToParent": "Retour a {Parent}"
  },
  "title": "Nom du module",
  "summary": "Une phrase accent resumant l'usage du module (tagline header — REQUISE)",
  "subtitle": "Description en 1-2 phrases",

  "header": {
    "businessRules": "regles metier",
    "apiEndpoints": "endpoints API"
  },

  "sections": {
    "objective": "Objectif",
    "access": "Acces & roles",
    "interface": "Apercu de l'interface",
    "createForm": "Formulaire de creation",
    "detailView": "Vue detail",
    "useCases": "Exemples d'utilisation",
    "features": "Ce que vous pouvez faire",
    "faq": "Questions frequentes",
    "technicalRef": "Reference technique (administrateurs) — cle requise UNIQUEMENT avec --tech"
  },

  "objective": "Description simple, redigee comme un objectif, de ce que permet le module (ce qu'il fait, a quoi il sert) — sans probleme/solution ni argumentaire",

  "access": {
    "columns": {
      "role": "Role",
      "actions": "Peut faire",
      "scope": "Portee"
    },
    "roles": {
      "admin": {
        "name": "Administrateur",
        "actions": "Consulter, creer, modifier, supprimer",
        "scope": "toutes"
      },
      "commercial": {
        "name": "Commercial",
        "actions": "Consulter, creer",
        "scope": "consulter : toutes · creer : les siennes"
      }
    },
    "unverified": "Droits declaratifs (rbac.md) — non verifies contre le seed"
  },

  "interface": {
    "sectionTitle": "Titre de la sous-section",
    "annotations": {
      "element1": "Explication de l'element visuel 1 du Mock UI",
      "element2": "Explication de l'element visuel 2 du Mock UI"
    }
  },

  "form": {
    "desc": "Ce formulaire permet de creer un nouveau...",
    "fieldLabel1": "Label du champ",
    "create": "Creer",
    "cancel": "Annuler",
    "annotations": {
      "field1": "Explication du champ 1",
      "field2": "Explication du champ 2",
      "actions": "Explication des boutons d'action"
    }
  },

  "detail": {
    "desc": "La vue detail s'ouvre lorsque...",
    "annotations": {
      "header": "Explication de l'en-tete",
      "tabs": "Explication des onglets",
      "stats": "Explication des indicateurs"
    }
  },

  "useCases": {
    "1": {
      "persona": "Sophie",
      "role": "Administratrice plateforme",
      "situation": "Sophie doit...",
      "withFeature": "Elle utilise..."
    }
  },

  "features": {
    "1": {
      "title": "Nom de la fonctionnalite",
      "description": "Explication claire",
      "example": "Exemple concret"
    }
  },

  "faq": {
    "1": {
      "question": "Question non-technique ?",
      "answer": "Reponse claire et concise."
    }
  },

  "techRef": {
    "businessRules": "Regles metier",
    "apiEndpoints": "Points de terminaison API",
    "rules": {
      "BR-001": "Description de la regle metier 1",
      "BR-002": "Description de la regle metier 2"
    }
  }
}
```

> **`access.roles.*`** : une entree par ligne de `report.accessRoles.rows`,
> UNIQUEMENT les lignes rapportees (le code fait foi — ne jamais inventer un
> droit). `name` = le libelle du role rapporte ; `actions` = les actions en
> formulation metier (read → Consulter, create → Creer, …) ; `scope` = la
> portee rapportee (ou le detail par action quand `porteeByAction` est mixte,
> ex. `"consulter : toutes · creer : les siennes"`). `access.unverified` n'est
> requis que quand `source === 'ba'`.
>
> **`techRef.*` + `sections.technicalRef`** : a generer UNIQUEMENT avec
> `--tech` (la section 9 est opt-in, OFF par defaut).

---

## Checklist Documentation (TYPE: user)

Avant de valider une documentation de module `user`, verifier:

- [ ] **Standalone TSX** (PAS DocRenderer, PAS doc-data.ts)
- [ ] **Lecture de la vraie page** TSX source AVANT generation du Mock UI
- [ ] **Mock UI Section 3: Interface** reproduisant fidelement la vraie page
- [ ] **Mock UI Section 4: Formulaire** avec champs du vrai formulaire
- [ ] **Mock UI Section 5: Vue detail** avec onglets/stats de la vraie page
- [ ] **Annotations** sur chaque section Mock UI (composant `Annotation`)
- [ ] **Header complet** : titre + **tagline accent `t('summary')`** (REQUISE — gate `missingRequired`) + sous-titre + **ligne de stats inline** (`{N} {t('header.xxx')}`, compteur en texte frere, PAS de pills, PAS de `{count}` i18n)
- [ ] **Resume descriptif** clair en 1 phrase (neutre, pas une accroche marketing)
- [ ] **Objectif** = Section 1 a part entiere (`t('sections.objective')` + `t('objective')`), decrit simplement ce qu'il fait / a quoi il sert — PAS de probleme/solution, PAS fondu dans le subtitle. (Gate step-03 : `missingRequired` doit etre vide.)
- [ ] **Section 2 Acces & roles** : table Role | Peut faire | Portee depuis `report.accessRoles.rows` UNIQUEMENT (le code fait foi, ne jamais inventer un droit) ; caveat `access.unverified` si `source === 'ba'` ; permissions `unmappedCodePermissions` signalees ; les warnings du join sont REPORTES a l'utilisateur, jamais publies. Fallback `source === 'none'` : carte simple URL + liste de permissions (comportement historique)
- [ ] **2-3 cas d'usage** avec personas nommes
- [ ] **5 fonctionnalites** avec exemples concrets
- [ ] **5 FAQ** non-techniques
- [ ] **Reference technique** (UNIQUEMENT si `--tech`) collapsible: regles metier + API endpoints reels — jamais supprimee d'un doc existant lors d'un re-run sans `--tech`
- [ ] **Theme tokens uniquement** (`var(--bg-secondary)`, `var(--color-accent-*)`, `var(--text-secondary)`, etc.) — JAMAIS de `bg-blue-50`, `border-green-200`, ni `--info-bg`/`--success-bg` pour les blocs d'emphase
- [ ] **Couleur de marque = `--color-accent-*`** (rampe thématisable qui suit l'éditeur de thème) — **JAMAIS `--color-primary-*`** (indigo figé, "flashy"). Badges/halos subtils = `bg-[var(--accent-bg)] text-[var(--accent-text)]`. Vérifier : `grep "color-primary" <fichier>` → 0 match
- [ ] **Graphiques & KPI dashboard = palette `--dataviz-*`** : séries via `--dataviz-accent`/`--dataviz-cat-N`, tendances via `--dataviz-trend-*` — jamais `--color-primary-*`/`--color-accent-*` pour les données
- [ ] **ZERO couleur Tailwind palette** : verifier `grep -E "(bg|text|border)-(blue|red|green|yellow|amber|purple|violet|gray|slate|zinc|indigo|teal|cyan|sky|orange|pink|rose|emerald|lime)-[0-9]"` → 0 match
- [ ] **Cards = pattern officiel** : `p-N bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[var(--radius-card)]` — PAS `className="card"`, PAS `rounded-lg`, PAS `bg-white`/`bg-gray-*`
- [ ] **Annotation discrete** : `bg-[var(--bg-secondary)] border-l-2 border-[var(--color-accent-500)]` — pas de boite encadree flashy
- [ ] **Charts fideles + expressifs** : type recharts respecte (histogramme vertical ≠ barres horizontales), ratio max/min ≥ 5x, hauteur ≥ h-24/h-20
- [ ] **Pas d'overflow DocPanel** : `overflow-hidden` sur cards de chart, `min-w-0` sur flex children, pas de width px
- [ ] **Racine sans `px-`** : `<div className="space-y-8 max-w-5xl mx-auto pb-12">` pour compatibilite DocPanel (~480px)
- [ ] **i18n** via `useTranslation()` — pas de texte hardcode
- [ ] **i18n 4 langues** : FR, EN, DE, IT (JSON plat, pas de cle racine)
- [ ] **PAS de section "Benefices"** ni "Avant/Apres"

---

## Types techniques (developer / database / testing)

Ces types n'ont PAS de template TSX ici : ils passent par le composant
`DocRenderer` de SmartStack.app (source mode uniquement — non exporte par
`@atlashub/smartstack`). Leur contrat de donnees (`doc-data.ts` : structure
`DocData`, wrapper de page, i18n imbrique sous la cle namespace, screenshots)
est defini dans [data-schema.md](data-schema.md).
