# GAB-141 · InteractiveCauseEffectLab — « Mini-lab cause→effet »

**Archétype / renderer_key :** `text_cta` (cartographie) · **module :** EdTechInteractiveLearning
**Critère validé :** changer le JSON change le lab sans modifier le HTML. ✅ check.py 12/12.

## Pack (structure officielle par-GAB)
```
GAB-141/
  renderer.html            ← moteur cause→effet (ne pas modifier par instance)
  instance.example.json    ← SOURCE DE VÉRITÉ (contenu réel, à plat)
  schema.contract.json     ← contrat de validation
  README-contract.md       ← ce fichier
```

## Archétype
**InteractiveCauseEffectLab** — mini-lab multifactoriel : l'élève active/désactive des causes (interrupteurs toggle) et observe un effet cumulatif monter via un compteur/barre de progression. Chaque cause a un poids (weight) ; la somme des poids actifs détermine le seuil de feedback affiché.

## Champs requis (instance, à plat)
`gab_id` · `cause_effect_id` · `instruction` · `items[]{id, icon, label, weight}` · `result{meter_label, thresholds[]{min, max, text}}`

Optionnels : `title`, `accessibility{keyboard_navigable, focus_visible, prefers_reduced_motion}`

## Ce qui vient du JSON vs HTML
- **JSON** : instruction, liste des causes (icône, libellé, poids), label du compteur, tous les seuils de feedback.
- **HTML** : interrupteurs visuels, barre de progression, logique de cumul, layout, BLOCKED.

## Garde-fous (child_safety)
- **BLOCKED** si `instruction` absente / `items` vides / `result.thresholds` absent / `gab_id` absent.
- Poids cumulés plafonnés à 100% — jamais de dépassement visuel.
- Aucun contenu pédagogique en dur dans le renderer.

## Comportement du moteur
1. L'élève clique (ou appuie Espace/Entrée) sur une cause → toggle `.on`.
2. Le moteur recalcule la somme des `weight` des causes `.on`.
3. Il recherche le threshold correspondant (`min ≤ total ≤ max`) et affiche le `text`.
4. La barre de progression se met à jour proportionnellement (max 100%).

## QA à vérifier
1. Modifier un `label`/`text` → rendu change sans toucher au HTML (critère d'or).
2. `items:[]` → BLOCKED propre.
3. Activer toutes les causes → barre = somme weights (plafonnée 100%) + dernier feedback.
4. `init(extInstance)` → le renderer adopte l'instance externe.
5. Navigation clavier : Tab entre causes + Espace/Entrée pour toggle.
6. Responsive 375/768/1024 — aucun débordement.

## Source
`INDEX-300-interactivelearning-GAB-141-145-PLAYABLE.html` (stage `data-tpl="141"`, handlers `celToggle`, `celUpdate`).
