{
  "contract_version": "gab_193_contract_v1",
  "gab_id": "GAB-193",
  "canonical_name": "PathMapNodeUnlockAnimation",
  "module_owner": "EdTechPathMap",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "unlock_animation_id",
    "title",
    "subtitle",
    "unlock_condition",
    "primary_cta"
  ],
  "optional_fields": [
    "feedback",
    "progress",
    "confetti",
    "burst_icon"
  ],
  "field_types": {
    "unlock_animation_id": "string — identifiant unique de l'instance d'animation",
    "title": "string — titre principal affiché en grand (ex : «Nouvelle mission débloquée !»)",
    "subtitle": "string — nom ou description du contenu désormais accessible",
    "unlock_condition": "object{label:string, status:enum['validated'], display:string} — prérequis validé côté serveur",
    "feedback": "object{heading:string, body:string} — explication pédagogique du déverrouillage",
    "progress": "object{label:string, current:integer, total:integer, percent:integer(0..100)}",
    "primary_cta": "object{label:string, action:string}",
    "confetti": "array<string> — émojis décoratifs animés (≤5)",
    "burst_icon": "string — émoji ou caractère pour l'animation centrale (défaut : 🔓)"
  },
  "constraints": [
    "unlock_condition.status DOIT être 'validated' — GAB-193 réservé aux nœuds réellement débloqués côté serveur.",
    "primary_cta : bouton structurel HTML, le libellé vient exclusivement du JSON.",
    "progress.percent dans [0,100] si présent.",
    "Ne PAS utiliser quand le déverrouillage est simulé côté front uniquement (→ GAB-039 UnlockBlock PlayKit).",
    "Ne PAS utiliser pour les animations de gain XP (→ PlayKit)."
  ],
  "blocked_conditions": [
    "title absent",
    "unlock_animation_id absent",
    "unlock_condition absent ou status != validated",
    "primary_cta absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria_label_sur_bouton_cta"
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "animation complète, CTA actif, 0 erreur" },
    { "case": "champ requis manquant (title absent)", "expected": "BLOCKED — message explicite" },
    { "case": "unlock_condition absent", "expected": "BLOCKED — nœud non déverrouillé côté serveur" },
    { "case": "feedback absent (optionnel)", "expected": "rendu sans bloc feedback, aucune erreur" },
    { "case": "progress absent (optionnel)", "expected": "rendu sans barre de progression, aucune erreur" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "prefers-reduced-motion actif", "expected": "animations réduites à 0.01ms, rendu intact" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, replay-btn block sur mobile" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-193",
    "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)."
  }
}
