# GAB-059 · StoryEpisodeSummary — « Résumé d'épisode »

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

## Pack (structure officielle par-GAB)
```
GAB-059/
  renderer.html            ← moteur résumé d'épisode (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` · `episode_summary_id` · `title` · `key_points[]{id,text}` · `primary_cta{label,action}`
Optionnels : `subtitle`, `section_label`.

## Ce qui vient du JSON vs HTML
- **JSON** : titre de l'épisode terminé, sous-titre, libellé de section, chaque point-clé à retenir, libellé et action du bouton CTA.
- **HTML** : layout de la carte, icône check centrale, animation cascade des points, état confirmé du bouton, fallback BLOCKED.

## Garde-fous (child_safety)
- **Anti-invention** : aucun texte pédagogique en dur dans le HTML — tout vient de l'instance.
- **BLOCKED** si `key_points` vide / `title` absent / `primary_cta` absent.
- **Animation cascade** pilotée par le moteur (délai 150ms + 200ms/point) — respecte `prefers-reduced-motion`.
- **CTA idempotent** : un seul clic possible (état confirmé irréversible, bouton désactivé).

## QA à vérifier
1. Modifier `title` / un `text` dans `key_points` → rendu change sans toucher au HTML (critère d'or).
2. `key_points:[]` → BLOCKED propre.
3. `title` absent → BLOCKED propre.
4. `primary_cta` absent → BLOCKED propre.
5. Clic CTA → bouton passe à l'état confirmé (texte changé, couleur mint, disabled).
6. Instance externe injectée via `ENGINE.init(ext)` → rendu change correctement.
7. Responsive 375/768/1024 — aucun débordement.

## Source
`INDEX-300-storylearning-GAB-055-059-PLAYABLE.html` (stage `data-tpl="59"`, handlers `esumNext`, `esumAnimate`, CSS `.esum-*`).

## Références externes (lot)
- GAB-058 (StoryCliffhanger) : cliffhanger avec suspense — `do_not_use_when` résumé de fin propre.
- GAB-021 : récap de session moteur — `do_not_use_when` résumé narratif d'épisode.
- GAB-052 : mini-leçon — `do_not_use_when` résumé d'épisode.
