# GAB-123 · ExerciseMatching — « Association de paires »

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

## Pack (structure officielle par-GAB)
```
GAB-123/
  renderer.html            ← moteur association de paires (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` · `matching_id` · `question` · `col_left_header` · `col_right_header` · `pairs[]{pair_key, left, right}`

Optionnels : `title`, `instruction_emoji`, `sub_instruction`, `feedback_ok`, `feedback_ok_detail`, `cta_label`.

## Ce qui vient du JSON vs HTML
- **JSON** : question, libellés des colonnes, texte de chaque item (gauche et droite), clé d'appariement `pair_key`, textes de feedback succès.
- **HTML** : grille 2 colonnes, logique de sélection/arming, animation `.wrong`/`.matched`, layout, BLOCKED.

## Archétype — ExerciseMatching (association 1↔1)
- Les items de la colonne droite sont **shufflés** par le moteur au rendu — jamais triviaux dans le HTML.
- La correspondance est portée par `pair_key` commun (identifiant, pas texte) : le moteur compare `sel.dataset.pair === el.dataset.pair`.
- Interaction Pattern B armé : sélection d'un item → colonne opposée s'arme (outline dashed) → instruction textuelle mise à jour → clic sur la cible → résolution.

## Garde-fous
- **BLOCKED** si `pairs` vide/absent, `question` absent, `col_left_header` ou `col_right_header` absent.
- Aucun texte de contenu pédagogique en dur dans le HTML — tout vient de l'instance.
- Feedback affiché uniquement quand **toutes** les paires sont correctement appariées.
- Animation `.wrong` (shake) en cas d'erreur, sans message de correction pour ne pas trivialiser.

## QA à vérifier
1. Modifier un `left`/`right`/header → rendu change sans toucher au HTML (critère d'or).
2. `pairs:[]` → BLOCKED propre.
3. Sélectionner un item L puis mauvais item R → animation shake, pas de matched.
4. Sélectionner toutes les bonnes paires → feedback_ok + feedback_ok_detail affichés.
5. Instance externe injectée via `ENGINE.init(ext)` → rendu change.
6. Responsive 375/768/1024 — sur mobile : colonnes empilées en 1 col.

## Source
`INDEX-300-exerciselearning-GAB-121-125-PLAYABLE.html` (stage `data-tpl="123"`, handlers `mtcPick`, `mtcArmingOn`, `mtcArmingOff`).
