{
  "contract_version": "gab_382_contract_v1",
  "gab_id": "GAB-382",
  "canonical_name": "MediaLearningImageViewer",
  "module_owner": "EdTechMediaLearning",
  "renderer_key": "media_viewer",
  "required_fields": [
    "gab_id",
    "image_viewer_id",
    "alt_text",
    "source_rights",
    "useful_zones"
  ],
  "optional_fields": [
    "image_src",
    "image_placeholder_emoji",
    "caption",
    "zoom_enabled",
    "fallback"
  ],
  "field_types": {
    "image_viewer_id": "string — identifiant unique de l'instance visionneuse",
    "alt_text": "string — texte alternatif accessible (lecture par screen reader)",
    "source_rights": "string — mention droits/crédits de l'image (ex: 'domaine public')",
    "image_src": "string|url — chemin ou URL de l'image réelle (optionnel : placeholder emoji si absent)",
    "image_placeholder_emoji": "string — emoji affiché si image_src absent",
    "caption": "string — légende courte affichée sous la scène",
    "zoom_enabled": "boolean — activer le bouton zoom (défaut true)",
    "useful_zones": "array<{zone_id,label,annotation,position:{top?,left?,bottom?,right?,width,height}}>",
    "fallback": "string — résumé textuel complet lisible sans l'image"
  },
  "constraints": [
    "alt_text obligatoire — aucune image sans description accessible.",
    "source_rights obligatoire — jamais afficher une image sans mention de droits.",
    "useful_zones doit contenir au moins 1 zone annotée.",
    "Si image_src absent, image_placeholder_emoji peut être utilisé pour le rendu de démo.",
    "zoom_enabled absent ou true → bouton zoom présent ; false → bouton masqué."
  ],
  "blocked_conditions": [
    "alt_text absent (BLOCKED)",
    "source_rights absent (BLOCKED)",
    "useful_zones vide ou absent (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "alt_text_obligatoire",
    "fallback_text"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet avec zones cliquables, zoom, alt, légende" },
    { "case": "alt_text absent", "expected": "BLOCKED listant le champ" },
    { "case": "source_rights absent", "expected": "BLOCKED listant le champ" },
    { "case": "useful_zones vide []", "expected": "BLOCKED listant le champ" },
    { "case": "clic zone annotée", "expected": "panel affiche l'annotation de la zone" },
    { "case": "bouton zoom", "expected": "toggle scale(1.25) / scale(1) sur la scène" },
    { "case": "bouton texte alternatif", "expected": "panel affiche alt_text complet" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-382",
    "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)."
  }
}
