# GAB-038 · ReplayBlock — « Revoir / relancer »

**Archétype / renderer_key :** `text_cta` (cartographie) · **module :** EdTechPlayKit
**Critère validé :** changer le JSON change le rendu (titre, texte, libellés, feedbacks) sans modifier le HTML. ✅ check.py 12/12.

## Pack (structure officielle par-GAB)
```
GAB-038/
  renderer.html            ← moteur ReplayBlock (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` · `replay_id` · `title` · `body` · `primary_cta{label,icon,action,feedback_text}`

Optionnels : `icon` (emoji picto central, défaut 🔁), `secondary_cta{label,icon,action,feedback_text}`.

## Ce qui vient du JSON vs HTML
- **JSON** : titre de la carte, corps explicatif, libellé + icône + texte de feedback de chaque bouton, icône centrale.
- **HTML** : structure de la carte, animation spin, layout des boutons, zone feedback, slots.

## Archétype
Carte de relance (replay) : proposer à l'apprenant de revoir un segment pédagogique ou de refaire une session complète. Le bouton primary est obligatoire (action principale, style violet gradient). Le bouton secondary est optionnel (style neutre).

## Garde-fous (child_safety)
- **Anti-invention** : aucun libellé, texte ou feedback n'est hardcodé dans le HTML.
- **BLOCKED** si `title`, `body` ou `primary_cta` sont absents.
- Le renderer affiche un message d'erreur explicite listant les champs manquants avec le mot BLOCKED.
- Animation `replay-spin` respecte `prefers-reduced-motion`.

## external_refs / dependencies
- **ENG-006** : reprise de session entamée (différent de ReplayBlock — ne pas confondre).
- **GAB-031** : action suivante neuve (≠ replay).
- **GAB-035** : déblocage (≠ replay).

## QA à vérifier
1. Modifier `title`/`body`/`primary_cta.label` → rendu change sans toucher au HTML (critère d'or).
2. `primary_cta` absent → BLOCKED propre avec libellé du champ manquant.
3. `secondary_cta` absent → rendu avec 1 seul bouton primary, pas d'erreur.
4. Clic primary → animation spin + `primary_cta.feedback_text` affiché.
5. Clic secondary → animation spin + `secondary_cta.feedback_text` affiché.
6. Instance externe injectée via `ENGINE.init(ext)` → rendu change.
7. Responsive 375/768/1024 (boutons empilés sur mobile).

## Source
`INDEX-300-playkit-GAB-036-040-PLAYABLE.html` (stage `data-tpl="38"`, handler `replayAct(type)`).
