{
  "contract_version": "gab_024_contract_v1",
  "gab_id": "GAB-024",
  "canonical_name": "GenericVideoBlock",
  "module_owner": "EdTechPlayKit",
  "renderer_key": "media_viewer",
  "required_fields": [
    "gab_id",
    "video_id",
    "title",
    "video_url",
    "fallback_text"
  ],
  "optional_fields": [
    "video_type",
    "thumbnail_url",
    "duration_label",
    "transcript"
  ],
  "field_types": {
    "video_id": "string — identifiant unique de la vidéo dans l'instance",
    "title": "string — titre affiché sous le player",
    "video_url": "string(url) — URL YouTube ou MP4 directe",
    "video_type": "enum['youtube','mp4','vimeo'] — type de source vidéo",
    "thumbnail_url": "string(url) | null — image de prévisualisation avant lecture",
    "duration_label": "string | null — durée affichable (ex: '4:32')",
    "fallback_text": "string — texte affiché si la vidéo est bloquée",
    "transcript": "string | null — transcription textuelle accessible"
  },
  "constraints": [
    "video_url obligatoire — sans URL le player est inutilisable.",
    "fallback_text obligatoire — conformité accessibilité et CNIL (aucun contenu sans alternative).",
    "title obligatoire — affiché sous le player, repris en aria-label.",
    "video_type doit être 'youtube', 'mp4' ou 'vimeo' si fourni."
  ],
  "blocked_conditions": [
    "video_id absent",
    "title absent",
    "video_url absent",
    "fallback_text absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "fallback_text si vidéo bloquée",
    "transcript optionnelle pour accessibilité complète"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, player visible, titre correct, 0 erreur" },
    { "case": "champ requis manquant (video_url absent)", "expected": "BLOCKED listant le champ" },
    { "case": "champ requis manquant (title absent)", "expected": "BLOCKED listant le champ" },
    { "case": "instance externe injectée via ENGINE.init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "clic sur le player", "expected": "état 'playing' : play-btn masqué, animation barres visibles" },
    { "case": "deuxième clic sur le player (pause)", "expected": "retour état initial, progression conservée" },
    { "case": "responsive 375/768/1024", "expected": "aspect-ratio 16/9 respecté, aucun débordement horizontal" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-024",
    "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)."
  }
}
