{
  "contract_version": "gab_032_contract_v1",
  "gab_id": "GAB-032",
  "canonical_name": "ShareOrSaveBlock",
  "module_owner": "EdTechPlayKit",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "share_or_save_id",
    "title",
    "body",
    "primary_cta"
  ],
  "optional_fields": [
    "icon",
    "secondary_cta",
    "save_feedback",
    "share_feedback"
  ],
  "field_types": {
    "title": "string — titre principal affiché centré",
    "body": "string — texte descriptif sous le titre",
    "icon": "string — emoji affiché dans le bloc icône centré",
    "primary_cta": "object{label:string, action:enum['save','share'], icon?:string}",
    "secondary_cta": "object{label:string, action:enum['save','share'], icon?:string}",
    "save_feedback": "string — message affiché après clic 'save' (couleur mint-deep)",
    "share_feedback": "string — message affiché après clic 'share' (couleur gold-deep)"
  },
  "constraints": [
    "primary_cta.action doit être 'save' ou 'share'.",
    "secondary_cta.action doit différer de primary_cta.action si présent.",
    "Le partage est conditionné aux règles du contexte (RGPD mineurs) — ne pas activer share sans contrôle d'autorisation externe.",
    "icon : emoji uniquement (string courte), pas d'URL d'image."
  ],
  "blocked_conditions": [
    "title absent",
    "body absent",
    "primary_cta absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible",
    "prefers_reduced_motion",
    "aria-live sur zone feedback"
  ],
  "child_safety": [
    "Le partage (action 'share') doit être conditionné à une autorisation externe — ne jamais exposer de lien sans vérification de contexte RGPD/mineur.",
    "Le feedback 'Lien de partage copié (selon règles du contexte)' rappelle explicitement la contrainte contextuelle."
  ],
  "qa_cases": [
    { "case": "instance conforme", "expected": "rendu complet avec titre, corps, 2 boutons, 0 erreur" },
    { "case": "champ requis manquant (title)", "expected": "BLOCKED listant le champ" },
    { "case": "champ requis manquant (primary_cta)", "expected": "BLOCKED listant le champ" },
    { "case": "clic bouton save", "expected": "feedback save affiché en mint-deep" },
    { "case": "clic bouton share", "expected": "feedback share affiché en gold-deep" },
    { "case": "instance externe injectée via init(ext)", "expected": "rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "aucun débordement horizontal, boutons wrappent correctement" },
    { "case": "secondary_cta absent", "expected": "rendu avec 1 seul bouton, pas d'erreur" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-032",
    "note": "Ce schema VALIDE l'instance. Contenu extrait du HTML source INDEX-300-playkit-GAB-031-035-PLAYABLE.html (bloc share-card, handler shareAct). Le contrat pédagogique complet vit dans le CORE-GAB officiel, pas ici."
  }
}
