# GAB-154 · GameActionChallenge — « Attrape les causes ! »

**Archétype / renderer_key :** `text_cta` (cartographie) · **module :** EdTechGameLearning
**Critère validé :** changer le JSON change le contenu du mini-jeu sans modifier le HTML. ✅ check.py 12/12.

## Pack (structure officielle par-GAB)
```
GAB-154/
  renderer.html            ← moteur mini-jeu action (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
```

## Champs requis (instance, à plat)
`gab_id` · `challenge_id` · `title` · `instruction` · `items[]{label,is_target:boolean}` · `feedback_init` · `feedback_ok` · `feedback_ko`

Optionnels : `lives` (défaut 3) · `feedback_game_over` · `feedback_win` (avec placeholders `{score}` et `{lives}`) · `primary_cta{label,action}`

## Ce qui vient du JSON vs HTML
- **JSON** : titre du HUD, consigne d'arène, chaque item (label + is_target), les 4 feedbacks, le libellé du bouton restart, le nombre de vies.
- **HTML** : HUD (score/vies/titre), arène avec flottants +1/-1, animations ok/ko, layout, fallback BLOCKED.

## Archétype
Mini-jeu d'action rapide à classement binaire : l'élève doit cliquer les items `is_target:true` (CAUSES) et éviter les items `is_target:false` (CONSÉQUENCES). Score local non noté, feedback immédiat, mécanique de vies.

## Garde-fous (child_safety)
- **Jeu NON NOTÉ** au bulletin — utiliser GAB-133 pour exercices chronométrés notés.
- **BLOCKED** si `items` vide / aucun `is_target:true` (jeu non finissable) / `instruction` absente.
- **Anti-hardcoding** : aucun texte pédagogique dans le HTML — tout vient de l'instance JSON.
- **Accessibilité** : `aria-label` sur chaque bouton cible, navigation clavier (Enter/Espace).

## QA à vérifier
1. Modifier un `label` ou `is_target` → rendu change sans toucher au HTML.
2. `items:[]` → BLOCKED propre, carte masquée.
3. Aucun `is_target:true` → BLOCKED.
4. Cliquer CAUSES → feedback_ok + score++.
5. Cliquer CONSÉQUENCES → feedback_ko + vie perdue.
6. Vies = 0 → feedback_game_over, jeu verrouillé.
7. Toutes les CAUSES trouvées → feedback_win avec {score}/{lives} interpolés.
8. Bouton restart → gaReset() repart de zéro.
9. Responsive 375/768/1024 — aucun débordement.

## external_refs / dependencies
- `GAB-133` : exercice chronométré NOTÉ (ne pas utiliser GAB-154 pour ce cas).
- `GAB-146` : tri exploratoire (ne pas utiliser GAB-154 pour ce cas).
- `GAB-155` : GameResultFeedback — écran de fin de partie (habituellement enchaîné après GAB-154).

## Source
`INDEX-300-gamelearning-GAB-151-155-PLAYABLE.html` (stage `data-tpl="154"`, handlers `gaPick`, `gaReset`, `gaFloat`).
