{
  "contract_version": "gab_073_contract_v1",
  "gab_id": "GAB-073",
  "canonical_name": "StoryGlossaryPopup",
  "module_owner": "EdTechStoryLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "glossary_id",
    "body",
    "terms"
  ],
  "optional_fields": [
    "title",
    "popup_note",
    "primary_cta"
  ],
  "field_types": {
    "body": "string — texte narratif contenant les mots à annoter",
    "terms": "array<{term:string, definition:string}> — liste des termes clés avec leur définition popup",
    "popup_note": "string — note pédagogique affichée sous le texte (invite à toucher un mot)",
    "primary_cta": "object{label:string, action:string}"
  },
  "constraints": [
    "terms doit contenir au moins 1 entrée sinon BLOCKED.",
    "Chaque terme doit avoir un champ 'term' et un champ 'definition' non vides.",
    "body doit être une chaîne non vide.",
    "Les termes annotés dans le body doivent correspondre aux entrées du tableau terms.",
    "Nombre de termes recommandé : 1 à 5 (au-delà = surcharge cognitive)."
  ],
  "blocked_conditions": [
    "terms absent ou vide",
    "body absent ou vide",
    "glossary_id absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "popup_closable_on_click_outside"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, termes soulignés cliquables, popup visible" },
    { "case": "terms absent ou vide", "expected": "BLOCKED listant le champ manquant" },
    { "case": "body absent", "expected": "BLOCKED body absent" },
    { "case": "clic sur un terme", "expected": "popup s'affiche avec le titre du terme (gold) + sa définition" },
    { "case": "clic en dehors d'un terme", "expected": "toutes les popups se ferment" },
    { "case": "instance externe injectée", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, popup réduite sur mobile" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-073",
    "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)."
  }
}
