{
  "contract_version": "gab_099_contract_v1",
  "gab_id": "GAB-099",
  "canonical_name": "VisualPosterHero",
  "module_owner": "EdTechVisualLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "poster_id",
    "highlight_plain",
    "highlight_em"
  ],
  "optional_fields": [
    "title",
    "icon",
    "subtitle",
    "renderer_key",
    "_note_dev"
  ],
  "field_types": {
    "poster_id": "string — identifiant kebab-case unique de l'affiche",
    "icon": "string — emoji unique affiché en grand (animé flottant), défaut '📢'",
    "highlight_plain": "string — partie normale du texte fort (avant la mise en valeur em)",
    "highlight_em": "string — partie mise en valeur (couleur or #FFB73D) du texte fort",
    "subtitle": "string — sous-titre contextuel sous le highlight, optionnel",
    "title": "string — intitulé interne de l'affiche (topbar/slot), optionnel"
  },
  "constraints": [
    "highlight_plain + highlight_em forment ensemble le message principal de l'affiche : ne pas fusionner en un seul champ (le split em/plain est structurel).",
    "icon doit être un emoji unique (1 seul caractère graphique), pas une URL.",
    "Une seule idée forte par affiche — ne pas empiler plusieurs messages (see do_not_use_when).",
    "poster_id en kebab-case, unique par usage pédagogique."
  ],
  "blocked_conditions": [
    "poster_id absent (BLOCKED)",
    "highlight_plain absent (BLOCKED)",
    "highlight_em absent (BLOCKED)"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "role=img + aria-label sur le poster-card (plain + em concaténés)",
    "aria-hidden sur l'emoji icon"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, poster violet avec icône animée, texte highlight + em or, sous-titre" },
    { "case": "poster_id absent", "expected": "BLOCKED listant poster_id absent" },
    { "case": "highlight_plain absent", "expected": "BLOCKED listant highlight_plain absent" },
    { "case": "highlight_em absent", "expected": "BLOCKED listant highlight_em absent" },
    { "case": "subtitle absent (optionnel)", "expected": "rendu valide sans zone sous-titre" },
    { "case": "icon absent (optionnel)", "expected": "fallback emoji 📢 utilisé, pas d'erreur" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375px", "expected": "font-size highlight réduit à 22px, pas de débordement" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-099",
    "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). Source : INDEX-300-visuallearning-GAB-096-100-PLAYABLE.html stage data-tpl='99'."
  }
}
