{
  "contract_version": "gab_228_contract_v1",
  "gab_id": "GAB-228",
  "canonical_name": "SmartSelectAccessibilityFit",
  "module_owner": "EdTechSmartSelect",
  "renderer_key": "text_cta",
  "required_fields": [
    "gab_id",
    "accessibility_fit_id",
    "title",
    "subtitle",
    "privacy_notice",
    "default_fit_key",
    "fits",
    "primary_cta"
  ],
  "optional_fields": [
    "impact_header",
    "secondary_cta",
    "fallback_text_summary"
  ],
  "field_types": {
    "fits": "array<{key:string, icon:string, label:string, description:string, impact:string}> — min 1 item",
    "default_fit_key": "string — doit correspondre à un key dans fits[]",
    "primary_cta": "object{label:string, action:string}",
    "secondary_cta": "object{label:string, action:string}"
  },
  "constraints": [
    "fits[] doit contenir au moins 1 item.",
    "default_fit_key doit référencer un key existant dans fits[].",
    "privacy_notice est obligatoire : doctrine non-stigmatisation A11y de Allo Education.",
    "Champ impact dans fits[] peut contenir du HTML limité (<b>). Jamais de scripts.",
    "Aucune donnée médicale dans l'instance — préférences UI uniquement (doctrine EdTechSmartSelect)."
  ],
  "blocked_conditions": [
    "fits vide ou absent",
    "accessibility_fit_id absent",
    "primary_cta absent"
  ],
  "accessibility": [
    "keyboard_navigable",
    "focus_visible_3px_violet",
    "prefers_reduced_motion",
    "aria_labels_on_fit_cards",
    "non_stigmatisant"
  ],
  "qa_cases": [
    { "case": "instance conforme (7 fits)", "expected": "grille 2 colonnes, fit par défaut sélectionné, impact affiché" },
    { "case": "fits vide", "expected": "BLOCKED listant 'fits vide ou absent'" },
    { "case": "accessibility_fit_id absent", "expected": "BLOCKED listant 'accessibility_fit_id absent'" },
    { "case": "primary_cta absent", "expected": "BLOCKED listant 'primary_cta absent'" },
    { "case": "clic sur un fit", "expected": "fit sélectionné visuellement, impact mis à jour depuis JSON" },
    { "case": "instance externe injectée via init(ext)", "expected": "le rendu change sans modifier le HTML" },
    { "case": "responsive 375/768/1024", "expected": "grille 1 col sur mobile, 2 col sur desktop, 0 débordement horizontal" }
  ],
  "traceability": {
    "derived_from_core_gab": "GAB-228",
    "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)."
  }
}
