{
  "contract_version": "gab_168_contract_v1",
  "gab_id": "GAB-168",
  "canonical_name": "AudioTranscriptSync",
  "module_owner": "EdTechAudioLearning",
  "renderer_key": "media_viewer",
  "required_fields": [
    "gab_id",
    "segments",
    "title"
  ],
  "optional_fields": [
    "audio_transcript_sync_id",
    "info_label",
    "info_text",
    "speech_lang",
    "speech_rate",
    "audio_src"
  ],
  "field_types": {
    "gab_id": "string — doit valoir 'GAB-168'",
    "title": "string — titre affiché dans le hero",
    "info_label": "string — libellé gras dans la bannière d'info (optionnel)",
    "info_text": "string — texte descriptif de la bannière d'info (optionnel)",
    "segments": "array<{index:number, text:string}> — liste ordonnée des segments du transcript",
    "speech_lang": "string BCP-47 — ex: 'fr-FR' (défaut: 'fr-FR')",
    "speech_rate": "number [0.1..2.0] — vitesse de synthèse vocale (défaut: 0.95)",
    "audio_src": "object{src:string}|{_TODO:string} — URL du fichier audio réel (peut porter _TODO si non disponible)"
  },
  "constraints": [
    "segments doit contenir au moins 1 élément.",
    "Chaque segment porte un index (entier >= 0) et un text (non vide).",
    "speech_rate dans [0.1, 2.0] si présent.",
    "audio_src peut être absent ou porter _TODO sans invalider l'instance.",
    "Le moteur utilise speechSynthesis comme fallback si audio_src absent."
  ],
  "blocked_conditions": [
    "gab_id absent (BLOCKED)",
    "segments vides ou absents (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable — chaque segment et chip est activable au clavier (Enter/Space)",
    "aria-label sur bouton lecture et segments",
    "focus_visible — outline violet sur focus",
    "prefers-reduced-motion — transitions réduites"
  ],
  "qa_cases": [
    { "case": "instance conforme 4 segments", "expected": "rendu complet, 4 spans surlignable, 4 chips nav, bouton lecture fonctionnel" },
    { "case": "segments vides []", "expected": "BLOCKED listé dans le panneau d'erreur" },
    { "case": "gab_id absent", "expected": "BLOCKED listé dans le panneau d'erreur" },
    { "case": "clic sur segment 2", "expected": "segment 2 devient active + chip 2 active + speechSynthesis déclenché" },
    { "case": "instance externe injectée via ENGINE.init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375px", "expected": "aucun débordement horizontal, player passe en colonne" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-168",
    "note": "Ce schema VALIDE l'instance. Le contrat pédagogique complet (input_contract / validation_logic / feedback_scoring_logic) vit dans le CORE-GAB officiel, pas ici (évite la duplication)."
  }
}
