{
  "version": "20260915c",
  "groups": {
    "mesures": {
      "scope": "Ces contrôles vérifient les facteurs et les conventions de conversion. Ils ne remplacent pas l’étalonnage d’un appareil ni la connaissance des conditions de mesure.",
      "cases": [
        {
          "slug": "hectare-m2",
          "title": "Une surface de 0,25 hectare",
          "inputs": "0,25 ha ; sens hectares vers m².",
          "answer": "2 500 m²",
          "why": "Le facteur de surface est 10 000 m² par hectare : 0,25 × 10 000 = 2 500.",
          "params": {
            "amount": 0.25,
            "direction": "forward"
          },
          "field": "raw.output",
          "expected": 2500,
          "error": false
        },
        {
          "slug": "litre-gallon",
          "title": "Un gallon impérial",
          "inputs": "1 gallon ; convention impériale ; sens gallons vers litres.",
          "answer": "4,54609 L",
          "why": "La convention impériale utilise 4,54609 litres par gallon. Elle est distincte du gallon liquide US.",
          "params": {
            "amount": 1,
            "direction": "reverse",
            "system": "imperial"
          },
          "field": "raw.output",
          "expected": 4.54609,
          "error": false
        },
        {
          "slug": "joule-watt",
          "title": "Une énergie répartie sur une minute",
          "inputs": "3 600 J ; durée 60 secondes ; sens joules vers watts.",
          "answer": "60 W",
          "why": "La puissance moyenne est 3 600 ÷ 60. Une durée est indispensable à cette conversion.",
          "params": {
            "amount": 3600,
            "duration": 60,
            "direction": "forward"
          },
          "field": "raw.output",
          "expected": 60,
          "error": false
        },
        {
          "slug": "celsius-kelvin",
          "title": "Une valeur sous le zéro absolu",
          "inputs": "−274 °C ; sens Celsius vers kelvins.",
          "answer": "Entrée refusée",
          "why": "Cette température donnerait un kelvin négatif dans le modèle utilisé. Le moteur doit refuser cette entrée.",
          "params": {
            "amount": -274,
            "direction": "forward"
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "duree": {
      "scope": "Les dates civiles, les durées fixes et les instants ne suivent pas les mêmes conventions. Ces exemples annoncent le calendrier et le sens du comptage ; les cas locaux dépendent en plus du fuseau du navigateur.",
      "cases": [
        {
          "slug": "entre-deux-dates",
          "title": "Passer par le 29 février",
          "inputs": "Du 28 février 2024 au 1er mars 2024.",
          "answer": "2 jours écoulés",
          "why": "Le calendrier 2024 comprend le 29 février. Le calcul mesure un écart de dates, sans ajouter un jour pour compter les deux bornes.",
          "params": {
            "startDate": "2024-02-28",
            "endDate": "2024-03-01"
          },
          "field": "raw.output",
          "expected": 2,
          "error": false
        },
        {
          "slug": "heures-travail",
          "title": "Déduire une pause",
          "inputs": "Début 09:00 ; fin 17:30 ; pause 60 minutes.",
          "answer": "7 h 30, soit 450 minutes",
          "why": "La présence de 510 minutes est diminuée de 60 minutes. Ce résultat arithmétique ne qualifie pas la rémunération de la pause.",
          "params": {
            "startTime": "09:00",
            "endTime": "17:30",
            "breakMinutes": 60
          },
          "field": "raw.output",
          "expected": 450,
          "error": false
        },
        {
          "slug": "jour-semaine-date",
          "title": "Vérifier un jour connu",
          "inputs": "1er janvier 2000 ; calendrier grégorien.",
          "answer": "Samedi",
          "why": "L’index du samedi vaut 6 dans le moteur de dates utilisé ; l’interface affiche le nom du jour.",
          "params": {
            "date": "2000-01-01"
          },
          "field": "raw.output",
          "expected": 6,
          "error": false
        },
        {
          "slug": "entre-deux-dates",
          "title": "Refuser une date inexistante",
          "inputs": "Du 29 février 2025 au 1er mars 2025.",
          "answer": "Entrée refusée",
          "why": "2025 n’est pas bissextile : le moteur ne doit pas transformer silencieusement le 29 février en une date de mars.",
          "params": {
            "startDate": "2025-02-29",
            "endDate": "2025-03-01"
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "finance": {
      "scope": "Les tests contrôlent les calculs selon les hypothèses annoncées. Une simulation réussie n’est ni une offre de crédit ni une garantie de rendement ; les frais, assurances et modalités du contrat doivent correspondre au scénario.",
      "cases": [
        {
          "slug": "credit-consommation",
          "title": "Un crédit sans intérêts",
          "inputs": "Capital 12 000 € ; taux nominal 0 % ; 48 mois ; frais 0 €.",
          "answer": "250 € par mois",
          "why": "À taux nul, le capital se répartit en 48 mensualités égales : 12 000 ÷ 48 = 250.",
          "params": {
            "principal": 12000,
            "annualRate": 0,
            "months": 48,
            "fees": 0
          },
          "field": "raw.payment",
          "expected": 250,
          "error": false
        },
        {
          "slug": "objectif-epargne",
          "title": "Constituer une somme à taux nul",
          "inputs": "Objectif 50 000 € ; départ 10 000 € ; 8 ans ; rendement 0 %.",
          "answer": "Environ 416,67 € par mois",
          "why": "Il reste 40 000 € à répartir sur 96 versements. La valeur interne vaut 40 000 ÷ 96 ; le montant affiché est arrondi.",
          "params": {
            "target": 50000,
            "initial": 10000,
            "annualRate": 0,
            "years": 8
          },
          "field": "raw.monthly",
          "expected": 416.6666666666667,
          "error": false
        },
        {
          "slug": "taux-interet-reel",
          "title": "Rendement et inflation identiques",
          "inputs": "Rendement nominal 3 % ; inflation 3 %.",
          "answer": "0 % de rendement réel",
          "why": "Le rapport 1,03 ÷ 1,03 vaut 1 : le gain de pouvoir d’achat du modèle est nul, avant les frais et impôts non inclus.",
          "params": {
            "nominalRate": 3,
            "inflationRate": 3
          },
          "field": "raw.real",
          "expected": 0,
          "error": false
        },
        {
          "slug": "credit-consommation",
          "title": "Un prêt sans durée",
          "inputs": "Capital 1 000 € ; taux 5 % ; durée 0 mois ; frais 0 €.",
          "answer": "Entrée refusée",
          "why": "Une annuité mensuelle ne peut pas être calculée sur zéro échéance. La validation doit intervenir avant la division.",
          "params": {
            "principal": 1000,
            "annualRate": 5,
            "months": 0,
            "fees": 0
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "taxes": {
      "scope": "Ces cas isolent des opérations arithmétiques avec des taux explicitement saisis. Ils ne prouvent pas l’éligibilité à un régime, une exonération ou un avantage fiscal. Les barèmes datés et les exclusions restent documentés sur chaque simulateur.",
      "cases": [
        {
          "slug": "calcul-tva",
          "title": "Ajouter une TVA de 20 %",
          "inputs": "100 € HT ; taux saisi 20 % ; sens HT vers TTC.",
          "answer": "120 € TTC",
          "why": "La taxe est 100 × 20/100 = 20 €, puis le TTC est 100 + 20 = 120 €.",
          "params": {
            "amount": 100,
            "rate": 20,
            "direction": "htToTtc"
          },
          "field": "raw.ttc",
          "expected": 120,
          "error": false
        },
        {
          "slug": "calcul-tva",
          "title": "Retrouver le HT",
          "inputs": "120 € TTC ; taux saisi 20 % ; sens TTC vers HT.",
          "answer": "100 € HT",
          "why": "L’inverse consiste à diviser par 1,20. Retirer 20 % du montant TTC ne retrouve pas la base initiale.",
          "params": {
            "amount": 120,
            "rate": 20,
            "direction": "ttcToHt"
          },
          "field": "raw.ht",
          "expected": 100,
          "error": false
        },
        {
          "slug": "tps-tvq",
          "title": "Additionner deux taxes sur la même base",
          "inputs": "100 $ avant taxes ; TPS 5 % ; TVQ 9,975 %.",
          "answer": "114,975 $ avant arrondi, soit 114,98 $ affichés",
          "why": "Les deux montants de taxe sont calculés sur 100 $, puis additionnés à cette base.",
          "params": {
            "amount": 100,
            "direction": "beforeToAfter",
            "gstRate": 5,
            "qstRate": 9.975
          },
          "field": "raw.after",
          "expected": 114.975,
          "error": false
        },
        {
          "slug": "calcul-tva",
          "title": "Un taux hors du domaine du formulaire",
          "inputs": "100 € HT ; taux saisi 101 %.",
          "answer": "Entrée refusée",
          "why": "La validation refuse un taux dépassant la limite annoncée par ce formulaire ; il s’agit d’un contrôle de saisie.",
          "params": {
            "amount": 100,
            "rate": 101,
            "direction": "htToTtc"
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "web": {
      "scope": "Les indicateurs sont des rapports entre volumes d’une même période et d’un même périmètre. Une formule correcte ne corrige pas un comptage erroné des clics, impressions ou dépenses ; elle ne prédit pas non plus les performances d’une campagne.",
      "cases": [
        {
          "slug": "taux-click",
          "title": "Un taux de clic de 0,5 %",
          "inputs": "1 250 clics ; 250 000 impressions.",
          "answer": "0,5 %",
          "why": "Le rapport 1 250 ÷ 250 000 est multiplié par 100 pour exprimer le pourcentage.",
          "params": {
            "clicks": 1250,
            "impressions": 250000
          },
          "field": "raw.ctr",
          "expected": 0.5,
          "error": false
        },
        {
          "slug": "cpm",
          "title": "Coût pour mille impressions",
          "inputs": "500 € dépensés ; 250 000 impressions.",
          "answer": "2 € de CPM",
          "why": "Le coût unitaire est multiplié par mille : 500 ÷ 250 000 × 1 000 = 2.",
          "params": {
            "cost": 500,
            "impressions": 250000
          },
          "field": "raw.cpm",
          "expected": 2,
          "error": false
        },
        {
          "slug": "cpc",
          "title": "Coût par clic",
          "inputs": "800 € dépensés ; 2 000 clics.",
          "answer": "0,40 € par clic",
          "why": "La dépense totale est divisée par le nombre de clics mesurés sur le même périmètre.",
          "params": {
            "cost": 800,
            "clicks": 2000
          },
          "field": "raw.cpc",
          "expected": 0.4,
          "error": false
        },
        {
          "slug": "taux-click",
          "title": "Des impressions absentes",
          "inputs": "5 clics ; 0 impression.",
          "answer": "Entrée refusée",
          "why": "Le taux serait une division par zéro : le moteur doit signaler l’absence de dénominateur au lieu d’afficher un pourcentage.",
          "params": {
            "clicks": 5,
            "impressions": 0
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "voiture": {
      "scope": "Les calculs dépendent des distances, consommations et prix fournis. Les contrôles démontrent la cohérence du modèle, pas la consommation future d’un véhicule ; trafic, conduite, relief et dépenses exclues peuvent changer un coût réel.",
      "cases": [
        {
          "slug": "cout-trajet",
          "title": "Coût d’un trajet simple",
          "inputs": "300 km ; 6,5 L/100 km ; 1,85 €/L ; 25 € de péages ; autres frais 0 € ; 1 trajet ; 1 personne.",
          "answer": "61,08 € affichés",
          "why": "Le carburant coûte 300 × 6,5/100 × 1,85 = 36,075 €. Avec 25 € de péages, le total vaut 61,075 € avant arrondi.",
          "params": {
            "distance": 300,
            "trips": 1,
            "consumption": 6.5,
            "energyPrice": 1.85,
            "tolls": 25,
            "other": 0,
            "passengers": 1
          },
          "field": "primary",
          "expected": "61,08 €",
          "error": false
        },
        {
          "slug": "consommation-carburant",
          "title": "Mesurer une consommation au plein",
          "inputs": "650 km parcourus ; 42 litres ; prix 1,85 €/L.",
          "answer": "6,46 L/100 km affichés",
          "why": "La consommation est 42 ÷ 650 × 100. Le prix sert au coût, mais n’intervient pas dans ce ratio de volume par distance.",
          "params": {
            "distance": 650,
            "liters": 42,
            "price": 1.85
          },
          "field": "primary",
          "expected": "6,46 L/100 km",
          "error": false
        },
        {
          "slug": "emprunt",
          "title": "Un crédit auto à taux nul",
          "inputs": "12 000 € ; taux 0 % ; durée 24 mois ; frais 0 €.",
          "answer": "500,00 € par mois",
          "why": "Sans intérêts ni frais, 12 000 ÷ 24 = 500. Il ne s’agit pas d’une offre de financement.",
          "params": {
            "principal": 12000,
            "rate": 0,
            "months": 24,
            "fees": 0
          },
          "field": "primary",
          "expected": "500,00 €",
          "error": false
        },
        {
          "slug": "cout-trajet",
          "title": "Un nombre de trajets non prévu",
          "inputs": "100 km ; trajets 3 ; 5 L/100 km ; 2 €/L ; frais 0 € ; 1 personne.",
          "answer": "Entrée refusée",
          "why": "Le champ distingue un aller simple d’un aller-retour. La valeur 3 dépasse les options prévues par ce formulaire.",
          "params": {
            "distance": 100,
            "trips": 3,
            "consumption": 5,
            "energyPrice": 2,
            "tolls": 0,
            "other": 0,
            "passengers": 1
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "sante": {
      "scope": "Il s’agit de contrôles logiciels sur des données fictives, pas d’une validation clinique. Une formule de population, un seuil ou un calendrier théorique ne suffisent pas à évaluer la situation médicale d’une personne.",
      "cases": [
        {
          "slug": "calcul-imc",
          "title": "Calculer un IMC adulte",
          "inputs": "Taille 170 cm ; poids 65 kg.",
          "answer": "IMC affiché : 22,5",
          "why": "La valeur avant arrondi vaut 65 ÷ (1,70 × 1,70). Le moteur classe la valeur non arrondie ; l’indicateur ne constitue pas un diagnostic.",
          "params": {
            "height": 170,
            "weight": 65
          },
          "field": "primary",
          "expected": "22,5",
          "error": false
        },
        {
          "slug": "calories-jour",
          "title": "Reproduire l’équation et le coefficient",
          "inputs": "Coefficient femme ; âge 35 ans ; taille 170 cm ; poids 70 kg ; activité 1,375.",
          "answer": "1 961 kcal/jour affichées",
          "why": "Mifflin–St Jeor donne 1 426,5 kcal/jour au repos. La multiplication par 1,375 donne 1 961,4375 avant arrondi : c’est une estimation, pas une prescription.",
          "params": {
            "sex": "female",
            "age": 35,
            "height": 170,
            "weight": 70,
            "activity": 1.375
          },
          "field": "primary",
          "expected": "1 961 kcal/jour",
          "error": false
        },
        {
          "slug": "date-menstruation",
          "title": "Ajouter un cycle théorique",
          "inputs": "Début le 3 août 2026 ; cycle de 28 jours ; durée des règles 5 jours.",
          "answer": "31 août 2026",
          "why": "La date estimée est obtenue par ajout de 28 jours à la date de départ. Un cycle réel peut varier ; ce repère ne prédit pas avec certitude la prochaine date.",
          "params": {
            "date": "2026-08-03",
            "cycle": 28,
            "duration": 5
          },
          "field": "primary",
          "expected": "31 août 2026",
          "error": false
        },
        {
          "slug": "calcul-imc",
          "title": "Une taille hors du périmètre adulte retenu",
          "inputs": "Taille 90 cm ; poids 65 kg.",
          "answer": "Entrée refusée",
          "why": "Ce formulaire adulte refuse une taille inférieure à sa limite de saisie. Une formule exécutée malgré une valeur hors domaine serait un défaut de validation.",
          "params": {
            "height": 90,
            "weight": 65
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "ludique": {
      "scope": "Les probabilités supposent des dés ou une pièce équilibrés et les conditions indiquées. Les tests de tirage aléatoire vérifient des propriétés, comme les bornes et l’absence de doublons ; ils ne doivent pas exiger une séquence gagnante précise.",
      "cases": [
        {
          "slug": "probabilite-des",
          "title": "Obtenir 7 avec deux dés",
          "inputs": "2 dés indépendants à 6 faces ; somme cible 7.",
          "answer": "1/6, soit environ 16,67 %",
          "why": "Six couples ordonnés sur 36 donnent 7 : (1,6), (2,5), (3,4), (4,3), (5,2) et (6,1).",
          "params": {
            "dice": 2,
            "sides": 6,
            "target": 7
          },
          "field": "raw.probability",
          "expected": 0.16666666666666666,
          "error": false
        },
        {
          "slug": "pile-ou-face",
          "title": "Trois piles sur trois lancers",
          "inputs": "3 lancers indépendants ; exactement 3 piles.",
          "answer": "1/8, soit 12,5 %",
          "why": "Avec une pièce équilibrée, la suite pile-pile-pile a pour probabilité (1/2)³.",
          "params": {
            "tosses": 3,
            "heads": 3
          },
          "field": "raw.probability",
          "expected": 0.125,
          "error": false
        },
        {
          "slug": "paradoxe-anniversaires",
          "title": "Au moins une coïncidence dans un groupe",
          "inputs": "23 personnes ; modèle de 365 dates équiprobables.",
          "answer": "Environ 50,73 %",
          "why": "On calcule le complément de la probabilité que toutes les dates soient différentes. Ce résultat dépend de l’hypothèse de répartition uniforme.",
          "params": {
            "people": 23
          },
          "field": "raw.probability",
          "expected": 0.5072972343239857,
          "error": false
        },
        {
          "slug": "probabilite-des",
          "title": "Une somme impossible",
          "inputs": "2 dés à 6 faces ; somme cible 13.",
          "answer": "Entrée refusée",
          "why": "La plus grande somme possible est 12. Le formulaire refuse la cible 13 dans son domaine de saisie.",
          "params": {
            "dice": 2,
            "sides": 6,
            "target": 13
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "horoscope": {
      "scope": "Cette rubrique distingue les conventions astrologiques des données astronomiques. Un test réussi confirme le fonctionnement de la table ou de la formule utilisée ; il ne valide pas scientifiquement une interprétation astrologique ou un score de compatibilité.",
      "cases": [
        {
          "slug": "signe-astrologique",
          "title": "Une date dans la table tropicale",
          "inputs": "Date de naissance : 12 octobre 1990.",
          "answer": "Balance",
          "why": "Cette date appartient à la plage Balance de la table conventionnelle du site. Le contrôle ne calcule pas une constellation astronomique ni un portrait personnel.",
          "params": {
            "birthDate": "1990-10-12"
          },
          "field": "raw.sign",
          "expected": "balance",
          "error": false
        },
        {
          "slug": "signe-astrologique",
          "title": "Vérifier une borne de signe",
          "inputs": "Date de naissance : 21 mars 2000.",
          "answer": "Bélier selon la table fixe",
          "why": "Le test contrôle l’inclusion de cette date dans la plage Bélier. Il ne remplace pas un calcul précis de l’entrée du Soleil à partir d’un instant et d’un lieu.",
          "params": {
            "birthDate": "2000-03-21"
          },
          "field": "raw.sign",
          "expected": "belier",
          "error": false
        },
        {
          "slug": "distances-planetaires",
          "title": "Une distance moyenne de référence",
          "inputs": "Objet sélectionné : Lune.",
          "answer": "384 400 km",
          "why": "Cette valeur est la moyenne Terre–Lune utilisée dans la table. Elle ne représente pas une distance instantanée ni une mesure effectuée par le site.",
          "params": {
            "body": "moon"
          },
          "field": "raw.km",
          "expected": 384400,
          "error": false
        },
        {
          "slug": "signe-astrologique",
          "title": "Refuser un jour inexistant",
          "inputs": "Date saisie : 30 février 2000.",
          "answer": "Entrée refusée",
          "why": "Même une table conventionnelle doit rejeter une date qui n’existe pas au calendrier.",
          "params": {
            "birthDate": "2000-02-30"
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    },
    "math": {
      "scope": "Les opérations suivent les priorités et le mode angulaire annoncés. Les résultats attendus ci-dessous sont des calculs simples établis séparément du moteur ; la batterie interactive couvre aussi les surfaces, pourcentages et outils pratiques.",
      "cases": [
        {
          "slug": "",
          "title": "Priorité de la multiplication",
          "inputs": "Expression : 2 + 3 × 4.",
          "answer": "14",
          "why": "La multiplication 3 × 4 est calculée avant l’addition de 2.",
          "params": {
            "expression": "2+3*4",
            "mode": "basic"
          },
          "field": "value",
          "expected": 14,
          "error": false
        },
        {
          "slug": "",
          "title": "Parenthèses prioritaires",
          "inputs": "Expression : (2 + 3) × 4.",
          "answer": "20",
          "why": "Les parenthèses donnent 5, puis la multiplication par 4 donne 20.",
          "params": {
            "expression": "(2+3)*4",
            "mode": "basic"
          },
          "field": "value",
          "expected": 20,
          "error": false
        },
        {
          "slug": "scientifique",
          "title": "Un sinus en degrés",
          "inputs": "Expression : sin(30) ; mode DEG.",
          "answer": "0,5",
          "why": "Le mode degrés interprète 30 comme 30°. Le même nombre saisi en radians représente un autre angle.",
          "params": {
            "expression": "sin(30)",
            "mode": "deg"
          },
          "field": "value",
          "expected": 0.5,
          "error": false
        },
        {
          "slug": "",
          "title": "Division par zéro",
          "inputs": "Expression : 1 ÷ 0.",
          "answer": "Erreur signalée",
          "why": "La division par zéro ne doit pas produire un nombre utilisable comme résultat d’une opération ordinaire.",
          "params": {
            "expression": "1/0",
            "mode": "basic"
          },
          "field": "",
          "expected": null,
          "error": true
        }
      ]
    }
  }
}
