# GAB-291 · OralLearningIntro — « Cadre de sécurité avant de parler »

**Archétype / renderer_key :** `text_cta` (cartographie) · **module :** EdTechOralLearning
**Critère validé :** changer le JSON change l'écran sans modifier le HTML.

## Pack (structure officielle par-GAB)
```
GAB-291/
  renderer.html            ← moteur cadre oral sécurisé (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
**OralLearningIntro** : écran d'introduction à une session orale. Présente le contexte, recueille le consentement micro (opt-in strict), affiche le fallback sans micro, prévisualise la rubrique d'évaluation, et propose les CTAs de démarrage (avec ou sans micro).

## Champs requis (instance, à plat)
`oral_id` · `title` · `oral_goal` · `oral_type` · `expected_output` · `start_cta{primary:{label,action}}`

Optionnels : `subtitle`, `estimated_duration`, `rubric_preview[]{criterion,detail}`, `micro_policy{label,toggles[]{id,label,default}}`, `recording_policy{default_record,rationale}`, `privacy_notice`, `fallback_no_micro{label,text}`, `source_metadata{level?,subject?,context?}`.

## Ce qui vient du JSON vs HTML
- **JSON** : titre, sous-titre, chips métadonnées, libellés des toggles, texte fallback, critères rubric, raison pédagogique opt-in, libellés des CTAs.
- **HTML** : layout micro panel, structure toggles switch, bloc fallback, grille rubric, reason block, boutons structurels, slots footer.

## Garde-fous (child_safety / doctrine orale)
- **Opt-in strict** : `micro_policy.toggles[*].default` doit être `false` — micro jamais activé sans consentement.
- **Non-enregistrement par défaut** : `recording_policy.default_record` = `false` — l'enregistrement ne se déclenche qu'avec double consentement (micro + record).
- **Fallback obligatoire** : si `micro_policy` présent → `fallback_no_micro` doit être fourni. Tout élève doit avoir une voie sans micro.
- **Rubric = prévisualisation** : aucun score ni note dans `rubric_preview` — liste de critères uniquement.
- **BLOCKED** si `oral_id` / `title` / `oral_goal` / `oral_type` / `expected_output` / `start_cta` absents.

## Doctrine transverse EdTechOralLearning
L'oral est anxiogène par défaut. GAB-291 ouvre la chaîne `291 Intro → 292 SpeechPlan → 293 Timer → 294 JuryQ → 295 SelfReview`. Sa responsabilité unique : installer la **sécurité psychologique** avant toute production. Sans cet écran, les gabarits suivants peuvent être mal initialisés.

## QA à vérifier
1. Modifier `title` / `subtitle` / libellé CTA → rendu change sans toucher au HTML (critère d'or).
2. Supprimer `oral_id` → BLOCKED propre avec message d'erreur.
3. `rubric_preview: []` ou absent → bloc rubric masqué, pas d'erreur.
4. Clic CTA primary → feedback panel `.panel.ok` + reason block affiché.
5. Toggle switch → classe `.on` bascule + `aria-pressed` mis à jour.
6. Responsive 375 / 768 / 1024 → aucun débordement, CTAs empilés sur mobile.

## Source
`INDEX-300-orallearning-GAB-291-295-PLAYABLE.html` (stage `data-tpl="291"`, bloc `<!-- GAB-291 INTRO -->`).

## Références lot (external_refs / dependencies)
- **GAB-292** (SpeechPlan) : consomme `oral_id` comme `practice_ref` pour la session de planification.
- **GAB-293** (PracticeTimer) : consomme `oral_type` + durée cible.
- **GAB-294** (JuryQuestion) : `oral_context_ref` pointe vers la session initiée par GAB-291.
- **GAB-295** (SelfReview) : `oral_attempt_ref` clôt la chaîne ouverte par GAB-291.
