{
  "contract_version": "gab_389_contract_v1",
  "gab_id": "GAB-389",
  "canonical_name": "MediaLearningMediaAnnotation",
  "module_owner": "EdTechMediaLearning",
  "renderer_key": "annotation_media",
  "required_fields": [
    "gab_id",
    "annotation_id",
    "media_scene",
    "layers",
    "anchors",
    "text_fallback"
  ],
  "optional_fields": [
    "title",
    "_note_dev",
    "accessibility",
    "child_safety"
  ],
  "field_types": {
    "media_scene": "object{alt:string, background_hint?:string}",
    "layers": "array<{layer_id:string, label:string, icon?:string, message_on:string, message_off:string}>",
    "anchors": "array<{anchor_id:string, layer_id:string, annotation_text:string, position:object{top?:string,left?:string,right?:string,bottom?:string,width:string,height:string}}>",
    "text_fallback": "string"
  },
  "constraints": [
    "Chaque anchor.layer_id doit correspondre à un layer.layer_id existant dans layers[].",
    "layers[] doit contenir au moins 1 entrée.",
    "anchors[] doit contenir au moins 1 entrée.",
    "text_fallback est obligatoire — garantit l'accessibilité sans visuel.",
    "media_scene.alt obligatoire pour accessibilité."
  ],
  "blocked_conditions": [
    "layers absent ou vide",
    "anchors absent ou vide",
    "text_fallback absent",
    "media_scene absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "text_fallback_always_available",
    "aria_label_on_zones"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, 0 erreur, layers cliquables" },
    { "case": "layers absent", "expected": "BLOCKED listant le champ" },
    { "case": "anchors absent", "expected": "BLOCKED listant le champ" },
    { "case": "text_fallback absent", "expected": "BLOCKED listant le champ" },
    { "case": "activation layer", "expected": "zone ancrée visible, message_on affiché" },
    { "case": "désactivation layer", "expected": "zone masquée, message_off affiché" },
    { "case": "clic zone ancrée", "expected": "annotation_text affiché, état completed" },
    { "case": "fallback texte activé", "expected": "text_fallback affiché dans le panel" },
    { "case": "instance externe injectée", "expected": "rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-389",
    "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)."
  }
}
