{
  "contract_version": "gab_160_contract_v1",
  "gab_id": "GAB-160",
  "canonical_name": "GameRewardBridge",
  "module_owner": "EdTechGameLearning",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "bridge_id",
    "reward_context",
    "primary_cta"
  ],
  "optional_fields": [
    "title",
    "banner_text",
    "game_result",
    "playkit_grant",
    "_note_dev"
  ],
  "field_types": {
    "bridge_id": "string — identifiant unique de l'instance de pont",
    "banner_text": "string — message contextuel affiché en bandeau informatif",
    "game_result": "object{icon:string, label:string, value:string} — résultat côté mini-jeu local",
    "playkit_grant": "object{icon:string, label:string, value:string} — récompense validée par PlayKit",
    "reward_context": "object{heading:string, text:string, highlight?:string} — bloc de confirmation gamification",
    "primary_cta": "object{label:string, action:string} — bouton de transition vers célébration officielle"
  },
  "constraints": [
    "reward_context.heading et reward_context.text sont obligatoires si reward_context est présent.",
    "primary_cta.label fournit le libellé du bouton structurel (HTML), jamais hardcodé.",
    "Ce composant est lecture seule : il ne déclenche PAS la récompense, il la présente après validation PlayKit.",
    "game_result et playkit_grant sont optionnels mais fortement recommandés pour l'affichage du flow."
  ],
  "blocked_conditions": [
    "bridge_id absent",
    "reward_context absent ou incomplet (heading ou text manquant)",
    "primary_cta absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_labels_sur_bouton_cta"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet, flow game→playkit visible, bouton actif, 0 erreur" },
    { "case": "bridge_id absent", "expected": "BLOCKED listant le champ manquant" },
    { "case": "reward_context absent", "expected": "BLOCKED listant reward_context" },
    { "case": "primary_cta absent", "expected": "BLOCKED listant primary_cta" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "game_result omis", "expected": "rendu dégradé gracieux avec valeurs fallback '—'" },
    { "case": "clic bouton CTA", "expected": "panel ok avec action confirmée" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, flèche pivotée à 90° sur mobile" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-160",
    "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)."
  },
  "external_refs": {
    "dependencies": [
      "GAB-038 — célébration officielle (destination du CTA primary_cta.action='open_celebration')",
      "GAB-155 — feedback de fin de jeu (upstream, pas downstream)",
      "PlayKit/Gamification — service externe validant la récompense avant affichage"
    ]
  }
}
