{
  "summary": "Audit exhaustif des blocks : mapper origine→composant, grouper par design distinct vs variant de contenu, produire la liste des blocks sélectionnables + plan BO",
  "agentCount": 7,
  "logs": [],
  "result": {
    "distinctDesigns": {
      "distinctDesigns": [
        {
          "name": "Hero (home, avec formulaire)",
          "component": "Hero — src/components/Hero.tsx",
          "schema": "hero (titre, lead, image) + formulaire estimation codé en dur. NOTE: HeroLayout générique NE produit PAS ce design (pas de formulaire)",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [
            "Hero"
          ],
          "thumbnailSource": "Home (/) — héros plein écran avec carte formulaire estimation à droite"
        },
        {
          "name": "Hero générique (CTAs + stats + badges)",
          "component": "Hero — src/components/blocks/Hero.tsx",
          "schema": "hero (eyebrow, titre AccentTitle, lead, image, 2 CTAs pill, badges, stats max 4, variante home/compact). NOTE: design rendu par défaut quand l'éditeur ajoute un block hero, proche de LocalHero (sans formulaire)",
          "category": "Héros",
          "alreadySelectable": true,
          "variantKeys": [
            "HeroLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un block HeroLayout — photo plein écran + titre + 2 boutons + barre de stats"
        },
        {
          "name": "LocalHero (héros local sans formulaire)",
          "component": "LocalHero — src/components/LocalHero.tsx",
          "schema": "hero (titre, lead, image, 2 CTA primary/secondary) + marquee logos. NOTE: bespoke pages locales, non câblé dans un registryKey WP",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [],
          "thumbnailSource": "Page locale (ex. /cool-roof-roissy) — héros colonne unique + 2 CTAs + marquee logos bas"
        },
        {
          "name": "ProductHero (split bicolore crème/navy)",
          "component": "ProductHero — src/components/product/ProductHero.tsx",
          "schema": "hero (eyebrow, tag, productName, tagline, accentColor, image, badges[], stats[], ctaPrimaire, ctaSecondaire). NOTE: les 4 ProductHero* = 1 design via makeProductHero (variants accentColor/productName)",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [
            "ProductHeroCovaTherm",
            "ProductHeroCovaThermLight",
            "ProductHeroCovaSeal",
            "ProductHeroCovaMetal"
          ],
          "thumbnailSource": "Page produit /solutions/covatherm — héros split-screen crème (gauche) / navy + photo + stats (droite)"
        },
        {
          "name": "IndustrieHero (hub /industrie)",
          "component": "IndustrieHero — src/components/industrie/IndustrieHero.tsx",
          "schema": "hero (eyebrow, titre custom hubHeroTitre, lead, image, stats->proofs, badges, 2 CTA) + barre cartes secteurs flottante. NOTE: même archétype que ProductHero mais sans stats + barre flottante en débordement",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieHero"
          ],
          "thumbnailSource": "Hub /industrie — héros split bicolore + barre de cartes secteurs en débordement bas"
        },
        {
          "name": "DistributionHero (photo split + lead form, logos grille)",
          "component": "DistributionHero — src/components/industries/DistributionHero.tsx",
          "schema": "hero (eyebrow, titre, lead, image, stats, ctaLabel) + formulaire 3 champs + bandeau 4 logos statiques. NOTE: fichier propre, même archétype que LogistiqueHero mais logos en grille statique",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionHero"
          ],
          "thumbnailSource": "/industries/distribution — héros photo full-bleed + carte formulaire droite + 4 logos en grille"
        },
        {
          "name": "LogistiqueHero (photo split + lead form, logos marquee)",
          "component": "LogistiqueHero — src/components/industries/LogistiqueHero.tsx",
          "schema": "hero (eyebrow, titre, lead, image, stats, ctaPrimaire->ctaLabel) + formulaire 3 champs + marquee 12 logos. NOTE: composant distinct de DistributionHero (props stats vs proofs), seule diff visuelle = marquee animé",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueHero"
          ],
          "thumbnailSource": "/industries/logistique — héros photo + carte formulaire droite + marquee logos animé bas"
        },
        {
          "name": "SectorHero (template secteurs, form 5 champs) [non câblé WP]",
          "component": "SectorHero (interne) — src/components/industries/SectorPageTemplate.tsx l.267",
          "schema": "hero photo split + lead form 5 champs (type bâtiment/climat/surface/année/email) + bandeau logos statique. NOTE: oneoff registry vide => PAS sélectionnable ni atteignable via registryKey",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [],
          "thumbnailSource": "Pages secteurs (Agricole/Collectivités/ERP/Tertiaire) — héros photo + formulaire 5 champs"
        },
        {
          "name": "RoofHero (template toitures, form 4 champs) [non câblé WP]",
          "component": "RoofHero (interne) — src/components/roof/RoofPageTemplate.tsx l.274",
          "schema": "hero photo split + lead form 4 champs (dont type toiture readonly) + rangée logos flex. NOTE: oneoff vide => non sélectionnable",
          "category": "Héros",
          "alreadySelectable": false,
          "variantKeys": [],
          "thumbnailSource": "Pages toitures (BacAcier/Fibrociment...) — héros photo + formulaire 4 champs + rangée logos"
        },
        {
          "name": "ProductShowcase (toggle variantes)",
          "component": "ProductShowcase — src/components/product/ProductShowcase.tsx",
          "schema": "variantes_produit (entete{badge,titre,intro}, variantes[]{nom,usage,description}). NOTE: seul consommateur bespoke de variantes_produit; pas d'équivalent générique (le layout est des cards pricing)",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "ProductShowcase"
          ],
          "thumbnailSource": "/solutions/covatherm — showcase navy avec toggle pills (CovaTherm 8/20) + packshot"
        },
        {
          "name": "CovaSealShowcase (positionnement)",
          "component": "CovaSealShowcase — src/components/product/covaseal/CovaSealShowcase.tsx",
          "schema": "texte_image (entete, contenu, image, cta, note). NOTE: bespoke CovaSeal (richOr garde défaut riche si WP==seed)",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaSealShowcase"
          ],
          "thumbnailSource": "/solutions/covaseal-20 — section positionnement texte+image"
        },
        {
          "name": "CovaMetalShowcase (positionnement)",
          "component": "CovaMetalShowcase — src/components/product/covametal/CovaMetalShowcase.tsx",
          "schema": "texte_image (entete, contenu, image, cta, note). NOTE: bespoke CovaMetal",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaMetalShowcase"
          ],
          "thumbnailSource": "/solutions/covametal-20 — section positionnement texte+image"
        },
        {
          "name": "CovaThermLightShowcase (positionnement)",
          "component": "CovaThermLightShowcase — src/components/product/covatherm-light/CovaThermLightShowcase.tsx",
          "schema": "texte_image (entete, contenu, image, cta, note). NOTE: bespoke CovaTherm Light",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightShowcase"
          ],
          "thumbnailSource": "/solutions/covatherm-light — section positionnement texte+image"
        },
        {
          "name": "CovaThermLightBeforeAfter (avant/après)",
          "component": "CovaThermLightBeforeAfter — src/components/product/covatherm-light/CovaThermLightBeforeAfter.tsx",
          "schema": "avant_apres (entete, imageAvant, imageApres, legendeAvant, legendeApres). NOTE: seul consommateur bespoke d'avant_apres; le générique AvantApresLayout produit un autre design (glow terracotta/teal)",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightBeforeAfter"
          ],
          "thumbnailSource": "/solutions/covatherm-light — paire d'images avant/après"
        },
        {
          "name": "SolutionSection (home, 3 cartes chiffres) [non câblé WP]",
          "component": "SolutionSection — src/components/SolutionSection.tsx",
          "schema": "3 cartes (chiffre+label+description), données bespoke, sans image ni lien. NOTE: bespoke home, non câblé WP",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [],
          "thumbnailSource": "Home (/) — section solution fond blanc, header centré + 3 cartes blanches"
        },
        {
          "name": "LocalSolutionSection (split texte/image) [non câblé WP]",
          "component": "LocalSolutionSection — src/components/LocalSolutionSection.tsx",
          "schema": "texte + 3 points + CTA + grande image arrondie (split 7/5). NOTE: bespoke pages locales, non câblé WP",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [],
          "thumbnailSource": "Page locale — section solution split texte (gauche) / image (droite)"
        },
        {
          "name": "DistributionSolution (2 cartes comparatives classique vs cool)",
          "component": "DistributionSolution — src/components/industries/DistributionSolution.tsx",
          "schema": "texte_image (h3UlPairs -> comparisons classic/cool + métriques, liste->keyPoints, note->transition). NOTE: composant distinct de LogistiqueSolution, design quasi identique",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionSolution"
          ],
          "thumbnailSource": "/industries/distribution — 2 cartes avant/après (classique vs cool roof) + 4 key-points"
        },
        {
          "name": "LogistiqueSolution (2 cartes comparatives, 3 key-points)",
          "component": "LogistiqueSolution — src/components/industries/LogistiqueSolution.tsx",
          "schema": "texte_image (h3UlPairs -> comparisons classic/cool + métriques, liste->keyPoints). NOTE: fichier propre, design quasi identique à DistributionSolution (3 key-points, pas de transition)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueSolution"
          ],
          "thumbnailSource": "/industries/logistique — 2 cartes comparatives + 3 key-points"
        },
        {
          "name": "SolutionsGrid (grille 4 cartes produit)",
          "component": "SolutionsGrid — src/components/SolutionsGrid.tsx",
          "schema": "grille de cartes produit pastel (vignette, nom, tagline, description, supports, lien). NOTE: GrilleSolutionsLayout y délègue quand aucun node produit => atteignable par défaut",
          "category": "Grille",
          "alreadySelectable": true,
          "variantKeys": [
            "GrilleSolutionsLayout"
          ],
          "thumbnailSource": "/solutions — grille 4 colonnes de cartes produit à fond pastel"
        },
        {
          "name": "SectorsGrid (grille 3 cartes-photo secteurs)",
          "component": "SectorsGrid — src/components/SectorsGrid.tsx",
          "schema": "grille_secteurs (entete badge/titre/intro), secteurs codés en dur, dernière carte navy 'Devenir applicateur'. NOTE: rendu via origine 'SectorsGrid' ET via GrilleSecteursLayout (délégation par défaut)",
          "category": "Grille",
          "alreadySelectable": true,
          "variantKeys": [
            "SectorsGrid",
            "GrilleSecteursLayout"
          ],
          "thumbnailSource": "Home (/) ou /industries — grille 3 cartes-photo immersives + carte navy applicateur"
        },
        {
          "name": "GrilleSolutions générique (cards/compact relationship)",
          "component": "GrilleSolutions — src/components/blocks/GrilleSolutions.tsx",
          "schema": "grille_solutions (entete, produits.nodes[] via ficheProduit: tagline, name, prix/garantie/SRI, lien). NOTE: variante du layout quand l'éditeur sélectionne des nodes (design ≠ SolutionsGrid statique)",
          "category": "Grille",
          "alreadySelectable": true,
          "variantKeys": [
            "GrilleSolutionsLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un GrilleSolutionsLayout avec produits sélectionnés (cards/compact)"
        },
        {
          "name": "GrilleSecteurs générique (cards navy manuel)",
          "component": "GrilleSecteurs — src/components/blocks/GrilleSecteurs.tsx",
          "schema": "grille_secteurs (mode manuel, secteurs.nodes[] -> cards navy, lien /industries/slug). NOTE: variante du layout en mode manuel (design ≠ SectorsGrid statique)",
          "category": "Grille",
          "alreadySelectable": true,
          "variantKeys": [
            "GrilleSecteursLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un GrilleSecteursLayout mode manuel — cards navy avec blobs"
        },
        {
          "name": "GrilleCards générique (3 thèmes)",
          "component": "GrilleCards — src/components/blocks/GrilleCards.tsx",
          "schema": "grille_cards (entete, cards[]{image|icone, titre, texte, lien}, colonnes 2/3/4, theme clair/sombre/accent, transition). NOTE: rendu générique du schema grille_cards le plus consommé",
          "category": "Grille",
          "alreadySelectable": true,
          "variantKeys": [
            "GrilleCardsLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un GrilleCardsLayout — grille cards centrée avec icônes lucide"
        },
        {
          "name": "ExpertiseSection (cards expertise icônes)",
          "component": "ExpertiseSection — src/components/ExpertiseSection.tsx",
          "schema": "grille_cards (cards titre/texte fusionnés sur defaultCards, icônes + carte mise en avant codées). NOTE: bespoke home",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "ExpertiseSection"
          ],
          "thumbnailSource": "Home (/) — grille de cartes d'expertise avec icônes + une carte mise en avant"
        },
        {
          "name": "BlogPreview (cards article)",
          "component": "BlogPreview — src/components/BlogPreview.tsx",
          "schema": "grille_cards (titre, texte->category, image, lien->href). NOTE: même schema qu'ExpertiseSection mais composant distinct (cards article avec image)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "BlogPreview"
          ],
          "thumbnailSource": "Home (/) — aperçu articles de blog, cards avec image"
        },
        {
          "name": "ProductAdvantages (avantages produit icônes lucide)",
          "component": "ProductAdvantages — src/components/product/ProductAdvantages.tsx",
          "schema": "grille_cards (entete, cards[]{icone,titre,texte} -> CARD_ICONS lucide). NOTE: design produit CovaTherm",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "ProductAdvantages"
          ],
          "thumbnailSource": "/solutions/covatherm — grille d'avantages avec icônes lucide"
        },
        {
          "name": "CovaSealAdvantages",
          "component": "CovaSealAdvantages — src/components/product/covaseal/CovaSealAdvantages.tsx",
          "schema": "grille_cards (entete, cards[]{icone,titre,texte}). NOTE: composant dédié CovaSeal vs ProductAdvantages",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaSealAdvantages"
          ],
          "thumbnailSource": "/solutions/covaseal-20 — grille avantages"
        },
        {
          "name": "CovaMetalAdvantages",
          "component": "CovaMetalAdvantages — src/components/product/covametal/CovaMetalAdvantages.tsx",
          "schema": "grille_cards (entete, cards[]{icone,titre,texte}). NOTE: composant dédié CovaMetal",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaMetalAdvantages"
          ],
          "thumbnailSource": "/solutions/covametal-20 — grille avantages"
        },
        {
          "name": "CovaThermLightAdvantages",
          "component": "CovaThermLightAdvantages — src/components/product/covatherm-light/CovaThermLightAdvantages.tsx",
          "schema": "grille_cards (entete, cards[]{icone,titre,texte}, iconOverrides Brain/Eye). NOTE: composant dédié CovaTherm Light",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightAdvantages"
          ],
          "thumbnailSource": "/solutions/covatherm-light — grille avantages"
        },
        {
          "name": "RelatedProducts (cards produits liés)",
          "component": "RelatedProducts — src/components/product/RelatedProducts.tsx",
          "schema": "grille_solutions (entete, produits.nodes[]{slug} résolus via allProducts). NOTE: les 4 RelatedProducts* = 1 design via makeRelated (variant currentProduct)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "RelatedProductsCovaTherm",
            "RelatedProductsCovaThermLight",
            "RelatedProductsCovaSeal",
            "RelatedProductsCovaMetal"
          ],
          "thumbnailSource": "Bas de page produit /solutions/covatherm — grille de produits liés"
        },
        {
          "name": "IndustrieProblem (cards frictions)",
          "component": "IndustrieProblem — src/components/industrie/IndustrieProblem.tsx",
          "schema": "grille_cards (entete badge/titre/intro, cards->frictions, sans images, titre accentTwoLine)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieProblem"
          ],
          "thumbnailSource": "Hub /industrie — grille de cartes frictions"
        },
        {
          "name": "IndustrieApplications (cards images)",
          "component": "IndustrieApplications — src/components/industrie/IndustrieApplications.tsx",
          "schema": "grille_cards (cards applications name/line/img, transition->ctaLabel)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieApplications"
          ],
          "thumbnailSource": "Hub /industrie — grille applications avec images"
        },
        {
          "name": "LogistiqueProblem (cards frictions images)",
          "component": "LogistiqueProblem — src/components/industries/LogistiqueProblem.tsx",
          "schema": "grille_cards (frictions avec images, strongIntro). NOTE: distinct de DistributionProblem (props frictions vs problems)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueProblem"
          ],
          "thumbnailSource": "/industries/logistique — grille frictions avec images"
        },
        {
          "name": "LogistiqueApplications (cards warehouse)",
          "component": "LogistiqueApplications — src/components/industries/LogistiqueApplications.tsx",
          "schema": "grille_cards (warehouseTypes avec images, titre plain)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueApplications"
          ],
          "thumbnailSource": "/industries/logistique — grille types d'entrepôts"
        },
        {
          "name": "DistributionProblem (cards problems + transition)",
          "component": "DistributionProblem — src/components/industries/DistributionProblem.tsx",
          "schema": "grille_cards (problems avec images, transition). NOTE: distinct de LogistiqueProblem (prop problems + transition)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionProblem"
          ],
          "thumbnailSource": "/industries/distribution — grille problems avec images"
        },
        {
          "name": "DistributionApplications (cards surfaces)",
          "component": "DistributionApplications — src/components/industries/DistributionApplications.tsx",
          "schema": "grille_cards (surfaces avec images, transition). NOTE: distinct (prop surfaces)",
          "category": "Grille",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionApplications"
          ],
          "thumbnailSource": "/industries/distribution — grille surfaces"
        },
        {
          "name": "SystemSection (couches système Cool Roof)",
          "component": "SystemSection — src/components/SystemSection.tsx",
          "schema": "etapes (etapes fusionnés sur defaultLayers title/description/expert, image 1re étape). NOTE: même schema etapes que ProcessSection, design distinct",
          "category": "Process",
          "alreadySelectable": false,
          "variantKeys": [
            "SystemSection"
          ],
          "thumbnailSource": "Home (/) — présentation des couches du système Cool Roof"
        },
        {
          "name": "ProcessSection (timeline process)",
          "component": "ProcessSection — src/components/ProcessSection.tsx",
          "schema": "etapes (etapes title/text, réassurance, vidéo embed). NOTE: même schema etapes que SystemSection, design distinct (timeline numérotée)",
          "category": "Process",
          "alreadySelectable": false,
          "variantKeys": [
            "ProcessSection"
          ],
          "thumbnailSource": "Home (/) — timeline du process d'intervention + vidéo"
        },
        {
          "name": "IndustrieMethod (méthode étapes)",
          "component": "IndustrieMethod — src/components/industrie/IndustrieMethod.tsx",
          "schema": "etapes (entete, etapes->steps, reassurance 'Titre — desc', titre accentInline)",
          "category": "Process",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieMethod"
          ],
          "thumbnailSource": "Hub /industrie — méthode/process en étapes"
        },
        {
          "name": "DistributionMethod (méthode + vidéo embed)",
          "component": "DistributionMethod — src/components/industries/DistributionMethod.tsx",
          "schema": "etapes (etapes->steps, reassurance->duration/bottomNote, videoUrl->embed YouTube). NOTE: distinct de IndustrieMethod (+ vidéo embed, titre custom)",
          "category": "Process",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionMethod"
          ],
          "thumbnailSource": "/industries/distribution — méthode en étapes + vidéo YouTube"
        },
        {
          "name": "Etapes générique (timeline 2 cols)",
          "component": "Etapes — src/components/blocks/Etapes.tsx",
          "schema": "etapes (entete, etapes[]{titre,texte,image}, reassurance, videoUrl lien). NOTE: rendu générique par défaut du schema etapes",
          "category": "Process",
          "alreadySelectable": true,
          "variantKeys": [
            "EtapesLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un EtapesLayout — titre sticky gauche + timeline numérotée droite"
        },
        {
          "name": "PriceComparisonSection (tableau multi-solutions)",
          "component": "PriceComparisonSection — src/components/PriceComparisonSection.tsx",
          "schema": "tableau_comparatif (4 colonnes covalba/clim/membrane/refection, features oui/non, prix, encart texteApres, CTA). NOTE: même schema que PriceVS",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "PriceComparisonSection"
          ],
          "thumbnailSource": "Home (/) — tableau comparatif 4 colonnes de solutions"
        },
        {
          "name": "PriceVSSection (duel 2 colonnes face-à-face)",
          "component": "PriceVSSection — src/components/PriceVSSection.tsx",
          "schema": "tableau_comparatif (left/right prix/durée/coût labellisés + bullets). NOTE: même schema que PriceComparisonSection mais design distinct (duel face-à-face)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "PriceVSSection"
          ],
          "thumbnailSource": "Home (/) — comparatif face-à-face 2 colonnes"
        },
        {
          "name": "DistributionValueDurability (comparatif durabilité/coût)",
          "component": "DistributionValueDurability — src/components/industries/DistributionValueDurability.tsx",
          "schema": "tableau_comparatif (options colonnes/lignes prix/tenue/coût 10 ans, texteApres->note)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionValueDurability"
          ],
          "thumbnailSource": "/industries/distribution — comparatif durabilité et coût réel"
        },
        {
          "name": "DistributionCEE (table primes par zone)",
          "component": "DistributionCEE — src/components/industries/DistributionCEE.tsx",
          "schema": "tableau_comparatif (zones zone/regions/prime, texteApres splitté, cta)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionCEE"
          ],
          "thumbnailSource": "/industries/distribution — table des primes CEE par zone géo"
        },
        {
          "name": "TechnicalSpecs (specs 2 colonnes v8/v20)",
          "component": "TechnicalSpecs — src/components/product/TechnicalSpecs.tsx",
          "schema": "specs_techniques (entete, colonnesSpecs[], lignesSpecs[]{label, valeurs[]{texte}}). NOTE: bi-colonne CovaTherm",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "TechnicalSpecs"
          ],
          "thumbnailSource": "/solutions/covatherm — tableau specs techniques 2 colonnes"
        },
        {
          "name": "CovaSealSpecs (specs 1 colonne)",
          "component": "CovaSealSpecs — src/components/product/covaseal/CovaSealSpecs.tsx",
          "schema": "specs_techniques (entete, colonnesSpecs[0], lignesSpecs[]{label, valeurs[0]}). NOTE: mono-colonne vs TechnicalSpecs bi-colonne",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaSealSpecs"
          ],
          "thumbnailSource": "/solutions/covaseal-20 — specs techniques mono-colonne"
        },
        {
          "name": "CovaMetalSpecs (specs 1 colonne)",
          "component": "CovaMetalSpecs — src/components/product/covametal/CovaMetalSpecs.tsx",
          "schema": "specs_techniques (entete, colonnesSpecs[0], lignesSpecs[]{label, valeurs[0]}). NOTE: mono-colonne CovaMetal",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaMetalSpecs"
          ],
          "thumbnailSource": "/solutions/covametal-20 — specs techniques mono-colonne"
        },
        {
          "name": "CovaThermLightSpecs (specs 1 colonne)",
          "component": "CovaThermLightSpecs — src/components/product/covatherm-light/CovaThermLightSpecs.tsx",
          "schema": "specs_techniques (entete, colonnesSpecs[0], lignesSpecs[]{label, valeurs[0]}). NOTE: mono-colonne CovaTherm Light",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightSpecs"
          ],
          "thumbnailSource": "/solutions/covatherm-light — specs techniques mono-colonne"
        },
        {
          "name": "CovaSealComparison (comparatif 3 colonnes)",
          "component": "CovaSealComparison — src/components/product/covaseal/CovaSealComparison.tsx",
          "schema": "tableau_comparatif (withC=true -> 3 colonnes a/b/c, entetesColonnes, lignesComparatif, texteApres)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaSealComparison"
          ],
          "thumbnailSource": "/solutions/covaseal-20 — comparatif 3 colonnes"
        },
        {
          "name": "CovaMetalComparison (comparatif 2 colonnes)",
          "component": "CovaMetalComparison — src/components/product/covametal/CovaMetalComparison.tsx",
          "schema": "tableau_comparatif (withC=false -> 2 colonnes a/b)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaMetalComparison"
          ],
          "thumbnailSource": "/solutions/covametal-20 — comparatif 2 colonnes"
        },
        {
          "name": "CovaThermLightComparison (comparatif 2 colonnes)",
          "component": "CovaThermLightComparison — src/components/product/covatherm-light/CovaThermLightComparison.tsx",
          "schema": "tableau_comparatif (withC=false -> 2 colonnes)",
          "category": "Comparatif",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightComparison"
          ],
          "thumbnailSource": "/solutions/covatherm-light — comparatif 2 colonnes"
        },
        {
          "name": "TableauComparatif générique",
          "component": "TableauComparatif — src/components/blocks/TableauComparatif.tsx",
          "schema": "tableau_comparatif (entete, entetesColonnes, lignesComparatif, colonneMiseEnAvant, texteApres, CTA, CellValue oui/non/-). NOTE: rendu générique par défaut",
          "category": "Comparatif",
          "alreadySelectable": true,
          "variantKeys": [
            "TableauComparatifLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un TableauComparatifLayout — table desktop + cards mobile"
        },
        {
          "name": "SpecsTechniques générique",
          "component": "SpecsTechniques — src/components/blocks/SpecsTechniques.tsx",
          "schema": "specs_techniques (entete, colonnesSpecs, lignesSpecs, colonneMiseEnAvant, ficheTechnique). NOTE: rendu générique par défaut",
          "category": "Comparatif",
          "alreadySelectable": true,
          "variantKeys": [
            "SpecsTechniquesLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un SpecsTechniquesLayout — grille comparative + lien fiche technique"
        },
        {
          "name": "TableauSituations générique",
          "component": "TableauSituations — src/components/blocks/TableauSituations.tsx",
          "schema": "tableau_situations (lignesSituations[]{situation,recommandation,justification}). NOTE: schema propre, aucun bespoke ne le consomme",
          "category": "Comparatif",
          "alreadySelectable": true,
          "variantKeys": [
            "TableauSituationsLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un TableauSituationsLayout — grille 2 cols de cards situation/reco"
        },
        {
          "name": "SocialProofSection (bandeau chiffres-clés)",
          "component": "SocialProofSection — src/components/SocialProofSection.tsx",
          "schema": "chiffres (figures[0]=note/rating, reste=stats value/label/sub)",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "SocialProofSection"
          ],
          "thumbnailSource": "Home (/) — bandeau de chiffres-clés / preuve sociale"
        },
        {
          "name": "IndustrieBenefits (cards bénéfices OU métrique)",
          "component": "IndustrieBenefits — src/components/industrie/IndustrieBenefits.tsx",
          "schema": "chiffres + grille_cards (cards->5 bénéfices OU métrique -40% depuis figures). NOTE: adaptateur double-rôle, une seule row porte origine",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieBenefits"
          ],
          "thumbnailSource": "Hub /industrie — 5 cartes bénéfices (ou métrique)"
        },
        {
          "name": "LogistiqueBenefits (cards impacts OU métrique)",
          "component": "LogistiqueBenefits — src/components/industries/LogistiqueBenefits.tsx",
          "schema": "chiffres + grille_cards (cards->4 impacts OU métrique -10°C). NOTE: double-rôle",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueBenefits"
          ],
          "thumbnailSource": "/industries/logistique — 4 cartes impacts (ou métrique -10°C)"
        },
        {
          "name": "DistributionBenefits (cards bénéfices OU métrique)",
          "component": "DistributionBenefits — src/components/industries/DistributionBenefits.tsx",
          "schema": "chiffres + grille_cards (cards->5 cartes OU métrique -8 à -10°C). NOTE: double-rôle",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionBenefits"
          ],
          "thumbnailSource": "/industries/distribution — 5 cartes bénéfices (ou métrique)"
        },
        {
          "name": "PressSection (bandeau logos presse)",
          "component": "PressSection — src/components/PressSection.tsx",
          "schema": "logos (logosCustom nom/image/lien filtrés, entete.badge->label). NOTE: rendu via origine 'PressSection' ET via LogosLayout (source='presse')",
          "category": "Réassurance",
          "alreadySelectable": true,
          "variantKeys": [
            "PressSection",
            "LogosLayout"
          ],
          "thumbnailSource": "Home (/) — bandeau logos presse 'on parle de nous'"
        },
        {
          "name": "TrustLogos (marquee clients)",
          "component": "TrustLogos — src/components/TrustLogos.tsx",
          "schema": "logos (source='clients' ou custom vide), marquee statique repo. NOTE: rendu par défaut de LogosLayout",
          "category": "Réassurance",
          "alreadySelectable": true,
          "variantKeys": [
            "LogosLayout"
          ],
          "thumbnailSource": "Bandeaux logos marquee (home/pages) — défilement de logos clients"
        },
        {
          "name": "Logos custom marquee (inline)",
          "component": "Logos — src/components/blocks/Logos.tsx",
          "schema": "logos (source='custom' + logosCustom -> marquee WpImage grayscale). NOTE: marquee propre du layout quand l'éditeur fournit des logosCustom",
          "category": "Réassurance",
          "alreadySelectable": true,
          "variantKeys": [
            "LogosLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un LogosLayout source='custom' avec logos saisis"
        },
        {
          "name": "CovaSealCertifications",
          "component": "CovaSealCertifications — src/components/product/covaseal/CovaSealCertifications.tsx",
          "schema": "certifications (entete, itemsCertifications[]{nom,description}, CERT_ICONS)",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaSealCertifications"
          ],
          "thumbnailSource": "/solutions/covaseal-20 — grille certifications"
        },
        {
          "name": "CovaMetalCertifications",
          "component": "CovaMetalCertifications — src/components/product/covametal/CovaMetalCertifications.tsx",
          "schema": "certifications (entete, itemsCertifications[]{nom,description})",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaMetalCertifications"
          ],
          "thumbnailSource": "/solutions/covametal-20 — grille certifications"
        },
        {
          "name": "CovaThermLightCertifications",
          "component": "CovaThermLightCertifications — src/components/product/covatherm-light/CovaThermLightCertifications.tsx",
          "schema": "certifications (entete, itemsCertifications[]{nom,description})",
          "category": "Réassurance",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightCertifications"
          ],
          "thumbnailSource": "/solutions/covatherm-light — grille certifications"
        },
        {
          "name": "Certifications générique",
          "component": "Certifications — src/components/blocks/Certifications.tsx",
          "schema": "certifications (entete, itemsCertifications[]{logo,nom,description}). NOTE: rendu générique par défaut",
          "category": "Réassurance",
          "alreadySelectable": true,
          "variantKeys": [
            "CertificationsLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un CertificationsLayout — grille 3 cols cards blanches logo+nom"
        },
        {
          "name": "Chiffres générique",
          "component": "Chiffres — src/components/blocks/Chiffres.tsx",
          "schema": "chiffres (entete, figures[]{value,label,sublabel}, theme sombre/clair). NOTE: rendu générique par défaut",
          "category": "Réassurance",
          "alreadySelectable": true,
          "variantKeys": [
            "ChiffresLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un ChiffresLayout — rangée de chiffres séparés par bordures"
        },
        {
          "name": "BarreReassurance générique",
          "component": "BarreReassurance — src/components/blocks/BarreReassurance.tsx",
          "schema": "barre_reassurance (items texte, icône Check uniforme). NOTE: schema propre, repris de ReassuranceBar",
          "category": "Réassurance",
          "alreadySelectable": true,
          "variantKeys": [
            "BarreReassuranceLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un BarreReassuranceLayout — bande sombre items + Check teal"
        },
        {
          "name": "Testimonials (carrousel témoignages vidéo)",
          "component": "Testimonials — src/components/Testimonials.tsx",
          "schema": "temoignages_video (youtubeId, citation, nom, role, entreprise, contexte)",
          "category": "Témoignage",
          "alreadySelectable": false,
          "variantKeys": [
            "Testimonials"
          ],
          "thumbnailSource": "Home (/) — carrousel de témoignages vidéo YouTube"
        },
        {
          "name": "IndustrieProof (preuve chiffres)",
          "component": "IndustrieProof — src/components/industrie/IndustrieProof.tsx",
          "schema": "chiffres (entete, figures), prop sector filtre les références. NOTE: IndustrieProof et IndustrieProofLogistique = MÊME composant (industrieProofAdapter, seule diff prop sector) => 1 design, 2 variants",
          "category": "Témoignage",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieProof",
            "IndustrieProofLogistique"
          ],
          "thumbnailSource": "Hub /industrie — bandeau preuve/références chiffrées"
        },
        {
          "name": "DistributionProof (preuve chiffres)",
          "component": "DistributionProof — src/components/industries/DistributionProof.tsx",
          "schema": "chiffres (entete, figures). NOTE: fichier/composant propre, distinct de IndustrieProof (pas de prop sector)",
          "category": "Témoignage",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionProof"
          ],
          "thumbnailSource": "/industries/distribution — bandeau preuve chiffrée"
        },
        {
          "name": "TemoignagesVideo générique",
          "component": "TemoignagesVideo — src/components/blocks/TemoignagesVideo.tsx",
          "schema": "temoignages_video (cards vidéo YouTube hqdefault, overlay citation hover). NOTE: rendu générique par défaut",
          "category": "Témoignage",
          "alreadySelectable": true,
          "variantKeys": [
            "TemoignagesVideoLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un TemoignagesVideoLayout — grille 3 cols cards vidéo navy"
        },
        {
          "name": "FAQSection (accordéon FAQ home)",
          "component": "FAQSection — src/components/FAQ.tsx",
          "schema": "faq (questions question->q/reponse->a, badge/titre/intro/CTA). NOTE: clé registry 'FAQ', composant exporté FAQSection",
          "category": "FAQ",
          "alreadySelectable": false,
          "variantKeys": [
            "FAQ"
          ],
          "thumbnailSource": "Home (/) — accordéon FAQ"
        },
        {
          "name": "ProductFAQ (FAQ produit)",
          "component": "ProductFAQ — src/components/product/ProductFAQ.tsx",
          "schema": "faq (entete, questions[]{question,reponse} dangerouslySetInnerHTML, cta). NOTE: les 4 ProductFAQ* = 1 design via makeFaq (variant fallback par produit)",
          "category": "FAQ",
          "alreadySelectable": false,
          "variantKeys": [
            "ProductFAQCovaTherm",
            "ProductFAQCovaThermLight",
            "ProductFAQCovaSeal",
            "ProductFAQCovaMetal"
          ],
          "thumbnailSource": "/solutions/covatherm — accordéon FAQ produit"
        },
        {
          "name": "IndustrieFAQ",
          "component": "IndustrieFAQ — src/components/industrie/IndustrieFAQ.tsx",
          "schema": "faq (entete, questions->faqs, cta, titre accentTwoLine)",
          "category": "FAQ",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieFAQ"
          ],
          "thumbnailSource": "Hub /industrie — FAQ"
        },
        {
          "name": "LogistiqueFAQ",
          "component": "LogistiqueFAQ — src/components/industries/LogistiqueFAQ.tsx",
          "schema": "faq (entete, questions->faqs, cta). NOTE: fichier propre, distinct de IndustrieFAQ/DistributionFAQ",
          "category": "FAQ",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueFAQ"
          ],
          "thumbnailSource": "/industries/logistique — FAQ"
        },
        {
          "name": "DistributionFAQ",
          "component": "DistributionFAQ — src/components/industries/DistributionFAQ.tsx",
          "schema": "faq (entete, questions->faqs, cta). NOTE: fichier propre",
          "category": "FAQ",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionFAQ"
          ],
          "thumbnailSource": "/industries/distribution — FAQ"
        },
        {
          "name": "Faq générique (accordéon + JSON-LD)",
          "component": "Faq — src/components/blocks/Faq.tsx",
          "schema": "faq (entete, questions[]{question,reponse}, cta, JSON-LD FAQPage). NOTE: rendu générique par défaut",
          "category": "FAQ",
          "alreadySelectable": true,
          "variantKeys": [
            "FaqLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un FaqLayout — titre sticky + accordéon shadcn"
        },
        {
          "name": "CTASection (CTA double bouton)",
          "component": "CTASection — src/components/CTASection.tsx",
          "schema": "cta (titre, texte, ctaPrimaire, ctaSecondaire). NOTE: bespoke home, le générique CtaLayout en est dérivé",
          "category": "CTA",
          "alreadySelectable": false,
          "variantKeys": [
            "CTASection"
          ],
          "thumbnailSource": "Home (/) — bloc CTA final 2 boutons"
        },
        {
          "name": "IndustrieFinalCTA",
          "component": "IndustrieFinalCTA — src/components/industrie/IndustrieFinalCTA.tsx",
          "schema": "cta (titre, texte, ctaPrimaire/ctaSecondaire, reassurances)",
          "category": "CTA",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustrieFinalCTA"
          ],
          "thumbnailSource": "Hub /industrie — CTA final"
        },
        {
          "name": "LogistiqueFinalCTA",
          "component": "LogistiqueFinalCTA — src/components/industries/LogistiqueFinalCTA.tsx",
          "schema": "cta (titre, texte, 2 CTA, reassurances). NOTE: composant propre",
          "category": "CTA",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueFinalCTA"
          ],
          "thumbnailSource": "/industries/logistique — CTA final"
        },
        {
          "name": "DistributionFinalCTA",
          "component": "DistributionFinalCTA — src/components/industries/DistributionFinalCTA.tsx",
          "schema": "cta (titre, texte, 2 CTA, reassurances). NOTE: composant propre",
          "category": "CTA",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionFinalCTA"
          ],
          "thumbnailSource": "/industries/distribution — CTA final"
        },
        {
          "name": "Cta générique (navy + blobs, délégation HubSpot)",
          "component": "Cta — src/components/blocks/Cta.tsx",
          "schema": "cta (titre AccentTitle, texte, double BlockCta, reassurances, variante banner, hubspotFormId->HubSpotForm). NOTE: rendu générique par défaut (repris de CTASection)",
          "category": "CTA",
          "alreadySelectable": true,
          "variantKeys": [
            "CtaLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un CtaLayout — section navy avec blobs SVG + double CTA"
        },
        {
          "name": "ConstatSection (texte+image constat)",
          "component": "ConstatSection — src/components/ConstatSection.tsx",
          "schema": "texte_image (wysiwyg parsé h3pPairs->blocs, titre accent foreground/30, note, image). NOTE: bespoke home",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "ConstatSection"
          ],
          "thumbnailSource": "Home (/) — section constat texte + image"
        },
        {
          "name": "CoolRoofExplainerSection (explicatif pédagogique)",
          "component": "CoolRoofExplainerSection — src/components/CoolRoofExplainerSection.tsx",
          "schema": "texte_image (1er <p> -> paragraphe, image, note, CTA). NOTE: bespoke, sert de base au TexteImageLayout mais design home propre",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "CoolRoofExplainerSection"
          ],
          "thumbnailSource": "Home (/) — section explicative Cool Roof texte+image"
        },
        {
          "name": "WinterObjectionSection (traitement objection)",
          "component": "WinterObjectionSection — src/components/WinterObjectionSection.tsx",
          "schema": "texte_image (<p> [0]intro/[1]para1/[2]para2, titre accent teal-vivid). NOTE: bespoke home",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "WinterObjectionSection"
          ],
          "thumbnailSource": "Home (/) — section objection hiver"
        },
        {
          "name": "RSESection (engagement environnemental)",
          "component": "RSESection — src/components/RSESection.tsx",
          "schema": "texte_image (strongBrItems fusionnés sur defaultRseItems, badge/titre/image). NOTE: bespoke home",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "RSESection"
          ],
          "thumbnailSource": "Home (/) — section RSE / engagement"
        },
        {
          "name": "ApplicatorsSection (recrutement applicateurs)",
          "component": "ApplicatorsSection — src/components/ApplicatorsSection.tsx",
          "schema": "texte_image (title/description, benefits depuis liste, image, CTA). NOTE: bespoke home",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "ApplicatorsSection"
          ],
          "thumbnailSource": "Home (/) — section recrutement réseau d'applicateurs"
        },
        {
          "name": "IndustriePrinciple (comparatif classique vs cool + image)",
          "component": "IndustriePrinciple — src/components/industrie/IndustriePrinciple.tsx",
          "schema": "texte_image (h3pPairs->classic/cool, image cool, liste->benefits, cta)",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "IndustriePrinciple"
          ],
          "thumbnailSource": "Hub /industrie — principe classique vs cool roof avec image"
        },
        {
          "name": "TexteImage générique (grille 12 cols)",
          "component": "TexteImage — src/components/blocks/TexteImage.tsx",
          "schema": "texte_image (eyebrow, h2 AccentTitle, intro, contenu WysiwygHtml, liste Check, BlockCta, note, positionImage). NOTE: rendu générique par défaut (repris de CoolRoofExplainerSection)",
          "category": "Showcase",
          "alreadySelectable": true,
          "variantKeys": [
            "TexteImageLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un TexteImageLayout — grille 12 cols image glow teal + texte"
        },
        {
          "name": "SEOContent (contenu SEO long-form)",
          "component": "SEOContent — src/components/SEOContent.tsx",
          "schema": "contenu_seo (sections titre+HTML détexturisé, sources label/url). NOTE: bespoke home",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "SEOContent"
          ],
          "thumbnailSource": "Bas de home (/) — bloc contenu SEO long-form"
        },
        {
          "name": "CoolRoofGuide (guide éditorial CovaTherm)",
          "component": "CoolRoofGuide — src/components/product/CoolRoofGuide.tsx",
          "schema": "contenu_seo (rend corps riche sans props si hash WP==seed 564420437, sinon HTML générique). NOTE: design éditorial distinct",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "CoolRoofGuide"
          ],
          "thumbnailSource": "/solutions/covatherm — guide éditorial Cool Roof"
        },
        {
          "name": "CovaSealEditorial",
          "component": "CovaSealEditorial — src/components/product/covaseal/CovaSealEditorial.tsx",
          "schema": "contenu_seo (rend sans props si hash WP==seed 1375046801). NOTE: bespoke CovaSeal",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaSealEditorial"
          ],
          "thumbnailSource": "/solutions/covaseal-20 — dossier éditorial"
        },
        {
          "name": "CovaMetalEditorial",
          "component": "CovaMetalEditorial — src/components/product/covametal/CovaMetalEditorial.tsx",
          "schema": "contenu_seo (rend sans props si hash WP==seed 2432885520). NOTE: bespoke CovaMetal",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaMetalEditorial"
          ],
          "thumbnailSource": "/solutions/covametal-20 — dossier éditorial"
        },
        {
          "name": "CovaThermLightEditorial",
          "component": "CovaThermLightEditorial — src/components/product/covatherm-light/CovaThermLightEditorial.tsx",
          "schema": "contenu_seo (rend sans props si hash WP==seed 2142459583). NOTE: bespoke CovaTherm Light",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "CovaThermLightEditorial"
          ],
          "thumbnailSource": "/solutions/covatherm-light — dossier éditorial"
        },
        {
          "name": "LogistiqueSEOContent (SEO sans intro affichée)",
          "component": "LogistiqueSEOContent — src/components/industries/LogistiqueSEOContent.tsx",
          "schema": "contenu_seo (intro h2 colonBreak, sections firstP, sources->sourceLinks). NOTE: n'affiche pas l'intro <p>",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "LogistiqueSEOContent"
          ],
          "thumbnailSource": "Bas de /industries/logistique — bloc SEO"
        },
        {
          "name": "DistributionSEOContent (SEO avec intro affichée)",
          "component": "DistributionSEOContent — src/components/industries/DistributionSEOContent.tsx",
          "schema": "contenu_seo (intro h2 + premier <p> affiché, sections, sources). NOTE: variante de Logistique mais composant propre (affiche intro <p>)",
          "category": "Autre",
          "alreadySelectable": false,
          "variantKeys": [
            "DistributionSEOContent"
          ],
          "thumbnailSource": "Bas de /industries/distribution — bloc SEO"
        },
        {
          "name": "ContenuSeo générique (dossier repliable)",
          "component": "ContenuSeo — src/components/blocks/ContenuSeo.tsx",
          "schema": "contenu_seo (intro WysiwygHtml, sections h3+wysiwyg, sources ol, <details> repliable). NOTE: rendu générique par défaut (repris de SEOContent)",
          "category": "Autre",
          "alreadySelectable": true,
          "variantKeys": [
            "ContenuSeoLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un ContenuSeoLayout — dossier éditorial max-w-4xl repliable"
        },
        {
          "name": "CompatibilityTabs (onglets supports produit)",
          "component": "CompatibilityTabs — src/components/product/CompatibilityTabs.tsx",
          "schema": "compatibilite_supports (entete, supports[]{type,detail,image}, noteCouverture). NOTE: les 4 CompatibilityTabs* = 1 design via makeCompatibilityTabs",
          "category": "Showcase",
          "alreadySelectable": false,
          "variantKeys": [
            "CompatibilityTabsCovaTherm",
            "CompatibilityTabsCovaThermLight",
            "CompatibilityTabsCovaSeal",
            "CompatibilityTabsCovaMetal"
          ],
          "thumbnailSource": "/solutions/covatherm — onglets de compatibilité supports + image"
        },
        {
          "name": "CompatibiliteSupports générique (tabs/grille)",
          "component": "CompatibiliteSupports — src/components/blocks/CompatibiliteSupports.tsx",
          "schema": "compatibilite_supports (entete, supports[]{type,detail,image}, noteCouverture, rendu tabs|grille). NOTE: rendu générique CLIENT simplifié vs product/CompatibilityTabs",
          "category": "Showcase",
          "alreadySelectable": true,
          "variantKeys": [
            "CompatibiliteSupportsLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un CompatibiliteSupportsLayout — onglets thumbnails + panneau image"
        },
        {
          "name": "VariantesProduit générique (cards pricing)",
          "component": "VariantesProduit — src/components/blocks/VariantesProduit.tsx",
          "schema": "variantes_produit (variantes[]{nom,description,garantie,SRI,usage}, dernière mise en avant). NOTE: rendu générique du schema; distinct du toggle ProductShowcase (même schema)",
          "category": "Showcase",
          "alreadySelectable": true,
          "variantKeys": [
            "VariantesProduitLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un VariantesProduitLayout — grille cards pricing, dernière en navy"
        },
        {
          "name": "AvantApres générique (paire glow terracotta/teal)",
          "component": "AvantApres — src/components/blocks/AvantApres.tsx",
          "schema": "avant_apres (imageAvant terracotta, imageApres teal, legendes). NOTE: rendu générique; design différent de CovaThermLightBeforeAfter bespoke",
          "category": "Showcase",
          "alreadySelectable": true,
          "variantKeys": [
            "AvantApresLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un AvantApresLayout — 2 images côte à côte glow terracotta/teal"
        },
        {
          "name": "Video générique (iframe YouTube + takeaways)",
          "component": "Video — src/components/blocks/Video.tsx",
          "schema": "video (titre, videoUrl iframe, pointsCles[]{label,texte}). NOTE: schema video propre (repris de VideoSection)",
          "category": "Showcase",
          "alreadySelectable": true,
          "variantKeys": [
            "VideoLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un VideoLayout — iframe YouTube + grille points clés"
        },
        {
          "name": "Citation générique (blockquote éditorial)",
          "component": "Citation — src/components/blocks/Citation.tsx",
          "schema": "citation (texte, auteur, role, photo ronde). NOTE: schema citation propre",
          "category": "Autre",
          "alreadySelectable": true,
          "variantKeys": [
            "CitationLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un CitationLayout — blockquote centré grand guillemet"
        },
        {
          "name": "Timeline générique (frise chronologique)",
          "component": "Timeline — src/components/blocks/Timeline.tsx",
          "schema": "timeline (etapes[]{annee,etape,titre,texte,image} alternées). NOTE: schema timeline propre (repris de QuiSommesNous)",
          "category": "Autre",
          "alreadySelectable": true,
          "variantKeys": [
            "TimelineLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un TimelineLayout — frise verticale cards alternées + chiffre watermark"
        },
        {
          "name": "ReferencesGrille générique (cards filtrables)",
          "component": "ReferencesGrille — src/components/blocks/ReferencesGrille.tsx",
          "schema": "references_grille (mode toutes/secteur/manuel, filtres, cards image+badge secteur+metrics). NOTE: CLIENT, alimenté par @/data/references",
          "category": "Grille",
          "alreadySelectable": true,
          "variantKeys": [
            "ReferencesGrilleLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un ReferencesGrilleLayout — grille de références filtrables"
        },
        {
          "name": "ComposantReact générique (escape hatch interactif)",
          "component": "ComposantReact — src/components/blocks/ComposantReact.tsx",
          "schema": "composant_react (composant: hubspot/screener/diagnostic/... -> bespoke ou Placeholder). NOTE: délégation pure CLIENT",
          "category": "Escape hatch",
          "alreadySelectable": true,
          "variantKeys": [
            "ComposantReactLayout"
          ],
          "thumbnailSource": "Aperçu BO d'un ComposantReactLayout — wrapper section + composant interactif/Placeholder"
        },
        {
          "name": "FicheChantier générique (fiche référence)",
          "component": "FicheChantier — src/components/blocks/FicheChantier.tsx",
          "schema": "fiche_chantier (rend le groupe ficheReference du post courant via prop fiche). NOTE: sans prop fiche injectée par le template reference, return null",
          "category": "Fiche",
          "alreadySelectable": true,
          "variantKeys": [
            "FicheChantierLayout"
          ],
          "thumbnailSource": "Page d'une référence (template 'reference') — bandeau infos chantier + metrics + blockquote + vidéo"
        }
      ],
      "totalDistinct": 90,
      "contentVariantsNote": "14 registryKeys sont de PURS variants de contenu regroupés dans 4 designs via des factories make* (même composant rendu, props différentes) : (1) les 4 ProductHero* -> 1 design ProductHero via makeProductHero (diff accentColor/productName/tagline) ; (2) les 4 CompatibilityTabs* -> 1 design CompatibilityTabs via makeCompatibilityTabs ; (3) les 4 ProductFAQ* -> 1 design ProductFAQ via makeFaq ; (4) les 4 RelatedProducts* -> 1 design RelatedProducts via makeRelated. Cas supplémentaire confirmé dans le code : IndustrieProof + IndustrieProofLogistique = MÊME composant IndustrieProof (industrieProofAdapter, seule diff = prop sector), soit 2 registryKeys -> 1 design (le 2e absorbe la row references_grille voisine). Au total : 18 registryKeys s'effondrent en 5 designs.",
      "summary": "VALIDATION DE L'EXEMPLE UTILISATEUR (confirmé par lecture du code) : Hero home (avec formulaire codé en dur, src/components/Hero.tsx), ProductHero (product/ProductHero.tsx), IndustrieHero (industrie/IndustrieHero.tsx), DistributionHero (industries/DistributionHero.tsx) et LogistiqueHero (industries/LogistiqueHero.tsx) sont 5 designs de héros DISTINCTS — fichiers/composants différents, à exposer séparément. À l'inverse les 4 ProductHeroCova* = UN SEUL design : src/components/origine/produits.tsx définit makeProductHero(d) qui rend le MÊME <ProductHero> en ne changeant que accentColor/productName/tagline (purs variants de contenu, 1 vignette). Même pattern factory pour les 4 CompatibilityTabs* (makeCompatibilityTabs), 4 ProductFAQ* (makeFaq), 4 RelatedProducts* (makeRelated), et IndustrieProof/IndustrieProofLogistique partagent IndustrieProof via industrieProofAdapter (diff = prop sector).\n\nARCHITECTURE (src/components/blocks/BlockRenderer.tsx) : deux niveaux de rendu. (1) registry `origine` (src/components/origine/{home,produits,industrie,oneoff}.tsx) -> composants BESPOKE pixel-perfect keyés par registryKey, seedés avec un champ `origine`, PAS choisis par l'éditeur dans une modale (alreadySelectable=false). (2) registry générique par layout (suffixe XxxLayout) -> composants src/components/blocks/* quand l'éditeur ajoute un block sans origine (alreadySelectable=true). Le registre `oneoff` est VIDE : SectorHero/RoofHero et les sous-composants internes de SectorPageTemplate.tsx / RoofPageTemplate.tsx ne sont câblés sur AUCUN registryKey — designs distincts mais inatteignables via le block builder.\n\nPOINT alreadySelectable CLÉ : (a) Le Hero home bespoke (avec formulaire estimation) n'est PAS produit par HeroLayout générique, qui rend un héros CTAs+stats+badges SANS formulaire (proche de LocalHero) — deux designs distincts. (b) LogosLayout/GrilleSecteursLayout/GrilleSolutionsLayout DÉLÈGUENT aux bespoke PressSection/TrustLogos/SectorsGrid/SolutionsGrid quand la config est vide => ces designs statiques SONT atteignables par défaut (alreadySelectable=true), et chaque layout offre EN PLUS une variante propre (cards navy / marquee custom / cards relationship) avec des nodes.\n\nDÉCOMPTE : 90 designs visuellement distincts. ~26 sont des rendus génériques de layout déjà sélectionnables dans la modale ; ~64 sont des composants bespoke aujourd'hui invisibles dans la modale (rendus via le champ origine ou sur des templates hard-codés, dont SolutionSection home / LocalHero / LocalSolutionSection / SectorHero / RoofHero non câblés). Doublons d'archétype (composants distincts mais visuellement quasi-identiques) : DistributionHero~LogistiqueHero~SectorHero~RoofHero (héros photo split + lead form, diff = bandeau logos + nb champs) ; ProductHero~IndustrieHero (split bicolore crème/navy) ; DistributionSolution~LogistiqueSolution (2 cartes comparatives) ; familles produit Showcase/Specs/Certifications/Advantages/Comparison/Editorial dupliquées en fichiers séparés par produit (CovaSeal/CovaMetal/CovaThermLight) bien que de structure identique."
    },
    "plan": "ACFE isn't vendored in the repo (installed on the WP server). That's fine — I have enough to write the plan. The key fact: a subfield with `'default_value'` set will pre-fill new rows, so a cloned-per-design layout can ship `origine` pre-filled. Let me verify the front type system handles arbitrary layout `__typename` suffixes (the matching is by suffix `endsWith(k)`), which constrains how new layouts must be named.\n\nI have all the information needed. Here is the implementation plan.\n\n---\n\n# PLAN — Rendre chaque design distinct sélectionnable dans la modale ACFE\n\n## Constats de code (vérifiés)\n\n- **`BlockRenderer.tsx`** route en 2 temps : si `section.origine` non vide et présent dans `origineRegistry` → composant bespoke pixel-perfect ; sinon fallback générique par suffixe `__typename.endsWith(\"XxxLayout\")`. Le champ `origine` est donc le seul aiguilleur.\n- **`helpers.php` / `cvb_layout()`** ajoute à CHAQUE layout un sous-champ `origine` (type `text`, **`readonly => 1`**, `show_in_graphql`, pas de `default_value`). Aujourd'hui il n'est rempli QUE par le seed (`wordpress/seed/lib.php` mappe `layout`→`acf_fc_layout` et écrit `origine` comme valeur de champ normale). Un éditeur qui ajoute un block via la modale obtient `origine` vide → rendu générique.\n- **Les adaptateurs factory (`makeProductHero`, `makeCompatibilityTabs`, `makeFaq`, `makeRelated`) sont DÉJÀ data-driven** : ils lisent `s.eyebrow/titre/lead/image/stats/badges/ctaPrimaire...` et ne retombent sur le défaut `d.*` que si le champ WP est vide. Donc un design produit choisi dans la modale serait **éditable** dès l'instant où `origine` est posée et que les champs du layout `hero` existent (ils existent).\n- **Risque de doublons d'archétype** : `DistributionHero`, `LogistiqueHero` etc. ont leur propre fichier mais des props presque identiques. La modale en exposera un par fichier (conforme à la demande : 90 designs distincts).\n- La modale ACFE est pilotée par `acfe_flexible_category` + `acfe_flexible_thumbnail` (URL) par layout. Le matching front est par **suffixe** `...Layout` du `__typename` → **toute nouvelle entrée de modale doit réutiliser le même jeu de champs/suffixe de schema** sous peine de casser le mapping.\n\n---\n\n## 1. Mécanisme recommandé : **(b) un layout ACFE par schema + un sélecteur de design qui pilote `origine`**\n\n**Recommandation : approche (b)**, PAS le clonage de layout par design.\n\nJustification :\n\n| Critère | (a) 1 layout par design (~90 layouts) | (b) 1 layout/schema + champ `design` (recommandé) |\n|---|---|---|\n| Nombre d'entrées WPGraphQL | ~90 types GraphQL `SectionsSections*Layout` → explosion du schema, recompilation lourde, collisions de noms de champs nested ACF (cf. mémoire « pièges WPGraphQL for ACF ») | reste à 26 types existants → schema stable, **zéro nouveau type GraphQL** |\n| Front matching (`endsWith(\"XxxLayout\")`) | Il faut soit dupliquer les composants génériques, soit re-router : chaque layout cloné garde le même schema mais un `__typename` différent → casse le `registryKeys.find` (faux positifs entre `HeroLayout` et `HeroCovaThermLayout`) | **inchangé** : 1 schema = 1 suffixe ; `origine` reste l'aiguilleur, ce qui est déjà l'architecture |\n| Parité pixel des pages seedées | OK mais 90 fichiers `layouts/*.php` à maintenir | OK : le seed continue d'écrire `origine` directement, rien ne change pour les pages existantes |\n| UX éditeur | 90 vignettes dans la modale, mais champs identiques répétés ; difficile à catégoriser | **90 vignettes** quand même (voir ci-dessous comment), champs cohérents par schema |\n| Maintenance | Ajouter un design = nouveau fichier PHP + recompiler GraphQL | Ajouter un design = 1 ligne dans un registre PHP + 1 entrée registry front |\n\n**Le point clé qui rend (b) capable d'afficher 90 vignettes distinctes** : ACFE permet, **par layout**, un seul `acfe_flexible_thumbnail`. Pour avoir une vignette PAR design tout en gardant 1 layout par schema, on transforme le **sélecteur de design en un champ `select` avec rendu visuel**, OU — plus simple et plus fidèle à la modale ACFE — on adopte une **variante hybride b′** :\n\n### Variante retenue : **b′ — layouts « façade » générés depuis un registre de designs, partageant le jeu de champs du schema**\n\nOn garde **un fichier de champs par schema** (les 26 `layouts/*.php` actuels deviennent des **field-set réutilisables**), et on **génère plusieurs entrées de layout** (donc plusieurs vignettes/catégories dans la modale) qui **pointent toutes vers le même set de sous-champs**, en figeant `origine` via `default_value`. Le nom interne du layout (et donc le `__typename`) reste **le suffixe du schema** pour ne pas casser le front — ce qui impose qu'ACFE autorise plusieurs entrées de modale pour un même `name` de layout. **ACF ne le permet pas** (clé/`name` de layout unique).\n\n**Donc la décision finale, propre et non ambigüe :**\n\n> **(b) strict** — UN layout ACFE par schema (les 26 actuels), AUGMENTÉ d'un sous-champ `design` (type `select`, `ui => 1`, `return_format => value`, `show_in_graphql`, **non readonly**) listant les designs compatibles avec ce schema. Le front lit `section.design` (nouveau champ) en priorité sur `origine` :\n> - `design` rempli par l'éditeur → mappe vers la clé `origineRegistry` correspondante.\n> - `design` vide → fallback `origine` (seed) → fallback générique.\n>\n> La **multiplicité des vignettes** est obtenue en activant, dans la modale ACFE, l'aperçu par option du select via un champ ACFE `select` stylé en cartes (ACFE « select » avec images n'est pas natif) **OU**, recommandé, en gardant la sélection de design **dans le panneau du block** (pas dans la modale d'ajout) : la modale ajoute le block « Hero » (1 vignette/schema), puis l'éditeur choisit le design exact dans un select illustré.\n\n**Compromis assumé** : la demande explicite « chaque design sélectionnable **dans la modale** ». Si la modale d'ajout DOIT montrer 90 vignettes, alors il faut (a)/(b′) avec layouts distincts — mais cela impose de **changer le matching front du suffixe vers une clé explicite**. Je recommande donc, **pour satisfaire littéralement la demande sans exploser GraphQL** :\n\n### Décision finale recommandée : **(b″) — layouts distincts par design, MAIS schema GraphQL partagé via un champ caché `schemaName` + matching front par `origine`/`design`, et désactivation du matching par suffixe**\n\nConcrètement :\n1. Chaque design = une entrée de layout ACFE (vignette + catégorie propres) → **90 vignettes dans la modale**.\n2. Tous les designs d'un même schema **réutilisent le même set de sous-champs** (via les helpers `cvb_*` factorisés), donc **mêmes noms de champs** → on **force le même `graphql_field_name`** sur les champs partagés. Pour éviter la collision de types nested WPGraphQL ACF, chaque layout reste un type GraphQL distinct (ACF le gère), mais les sous-champs portent des `graphql_field_name` identiques (déjà le cas via `cvb_camel`).\n3. **`origine` reçoit un `default_value` figé** par design (le nom du composant), reste `readonly`. Plus besoin de champ `design` séparé : `origine` EST le sélecteur, pré-rempli par le layout choisi.\n4. **Front** : on remplace le matching par suffixe par un matching `origine`-first déjà présent. Le fallback générique par suffixe doit être ré-ancré : on ajoute sur chaque layout un sous-champ `schema` (texte readonly, `default_value` = nom du schema, ex. `\"hero\"`) exposé en GraphQL ; `BlockRenderer` matche `registry[schema]` au lieu de `typename.endsWith(...)`. Cela découple le nom du layout du composant générique.\n\nC'est la solution qui satisfait **littéralement la demande (90 vignettes en modale)** tout en gardant **un seul composant générique par schema** et **zéro réimplémentation**.\n\n---\n\n## 2. Designs bespoke éditables (pas figés)\n\nTrois cas selon l'état actuel de l'adaptateur :\n\n**a) Adaptateurs déjà data-driven (la majorité : factories `make*`, et les composants industrie qui lisent les champs)** — rien à faire côté React. Dès que `origine` est posée par le layout choisi, les champs du schema (`hero`, `grille_cards`, etc.) pilotent le rendu. Le seul travail : **vérifier que chaque adaptateur lit bien tous les champs du schema et ne court-circuite pas avec un `richOr(...) → défaut` quand l'éditeur part de zéro**. Pour un block neuf (champs vides), l'adaptateur retombe sur ses `d.*` defaults → l'éditeur voit le design « pré-rempli » de contenu d'exemple qu'il peut écraser. C'est le comportement voulu.\n\n**b) Adaptateurs qui rendent du contenu « riche » figé tant que le hash WP == seed (`CoolRoofGuide`, `*Editorial`, `richOr`)** — ces composants ignorent le champ tant qu'il vaut le seed et rendent un JSX riche codé en dur. Pour les rendre **réellement éditables** sur un nouveau block : l'éditeur saisit un contenu ≠ seed → `richOr` renvoie la valeur WP → rendu data-driven. **Acceptable tel quel** : un nouveau block n'a pas le contenu seed, donc il sera data-driven d'office. Aucune modif requise, juste documenter que le design éditorial « riche » n'apparaît que sur le contenu seed.\n\n**c) Designs sans adaptateur (registry `oneoff` vide : `SectorHero`, `RoofHero`, `SolutionSection`, `LocalHero`, `LocalSolutionSection`)** — pour les exposer, il faut **écrire un adaptateur** dans `src/components/origine/oneoff.tsx` qui mappe le schema le plus proche (ex. `hero` pour `LocalHero`/`SectorHero`) vers les props du composant, sur le modèle de `makeProductHero`. C'est le seul vrai travail React. Pour les composants avec des sous-structures non couvertes par le schema existant (ex. `SectorHero` form 5 champs), soit :\n- on accepte que les champs non-mappables prennent leurs **defaults codés** (form figé, reste éditable) ;\n- soit on étend le schema `hero` avec des sous-champs optionnels (déconseillé : pollue tous les heroes).\n\n**Recommandation pour (c)** : adaptateur « défauts paramétrés » — mappe ce que le schema permet, laisse le reste en défaut. Pas d'extension de schema. Si un design est trop spécifique (form 5 champs), il reste sélectionnable mais partiellement éditable, ce qui est cohérent avec l'esprit « parité d'abord ».\n\n---\n\n## 3. Vignettes : une par design distinct\n\nLe script `scripts/block-thumbnails/capture.mjs` capture **par layout** (`main > *`, mapping index→`typeToLayout`). Avec l'approche (b″), il faut capturer **par design (par `origine`)**, pas par layout. Modifications :\n\n1. Dans `capture.mjs`, remplacer la clé de capture `typeToLayout(__typename)` par **`section.origine`** (déjà lu dans `SECTIONS_SEL` via `ORIGINE_FRAG`). Fichier de sortie : `public/block-thumbnails/{origine}.jpg`.\n2. La liste cible `ALL_LAYOUTS` devient `ALL_DESIGNS` = toutes les clés de `origineRegistry` + les 26 génériques. Source de vérité : exporter la liste depuis un module partagé (cf. §4) pour ne pas la dupliquer.\n3. **Designs génériques** (pas de page seedée les utilisant) : il n'existe pas de page réelle. Deux options :\n   - créer une **page de galerie WP** (brouillon) qui empile un block par schema **sans `origine`** → rend le générique → capturé sur `/preview/gallery` (cf. `capture-gallery.mjs` déjà présent, à inspecter/étendre).\n   - pour les designs bespoke sans page (ex. `SectorHero` non câblé) : capturer sur la page d'origine si elle existe encore (`/cool-roof-roissy`, pages secteurs), sinon générer une vignette de remplacement via `gpt-image-2` (loi de préséance) ou exclure de la modale.\n4. Lancer : dev server sur `:3083` puis `node scripts/block-thumbnails/capture.mjs`, copier vers `wp-content/uploads/block-thumbnails/` (le PHP lit `wp_upload_dir()['baseurl'] . 'block-thumbnails/'`). Vérifier que l'upload est synchronisé côté serveur WP (cf. mémoire infra).\n\n---\n\n## 4. Fichiers à modifier/créer + ordre + pièges\n\n**Source de vérité unique (créer en premier)** :\n- **CRÉER `wordpress/mu-plugins/covalba-core/inc/field-groups/designs.php`** : un registre PHP `cvb_designs()` retournant, par design : `['origine' => 'ProductHeroCovaTherm', 'schema' => 'hero', 'label' => 'Hero produit (split crème/navy)', 'category' => 'Héros', 'thumb' => 'ProductHeroCovaTherm']`. ~90 entrées. C'est l'unique endroit à éditer pour ajouter un design.\n- **CRÉER `src/components/origine/designs.ts`** : miroir TS (ou généré depuis le PHP via un petit script) listant `{ origine, schema }` pour piloter `capture.mjs` et valider la cohérence registry↔PHP.\n\n**WordPress (ordre 1)** :\n1. **`helpers.php`** — modifier `cvb_layout()` : ajouter un sous-champ `schema` (text, readonly, `default_value` = `$name`, `show_in_graphql`, `graphql_field_name => 'schema'`). Garder `origine` mais lui permettre un `default_value` injectable.\n2. **`sections.php`** — remplacer la boucle `$files` (26) par : pour chaque entrée de `cvb_designs()`, générer une entrée de layout dont (i) le `name` doit rester **unique** ET garder un suffixe exploitable : ex. `name = $design['origine']` (PascalCase→`name`), `label` = `$design['label']`, sous-champs = ceux du schema `$design['schema']` (require du field-set), `origine` figée via `default_value`, `schema` figé. Catégorie = `$design['category']`. Vignette = `$thumb_base . $design['thumb'] . '.jpg'`.\n   - **Piège GraphQL** : chaque layout devient un type `SectionsSections{Name}Layout`. 90 types. Pour limiter le risque de collision des **sous-champs nested** (cf. mémoire « pièges WPGraphQL for ACF »), garder les `graphql_field_name` des sous-champs **identiques au schema** (déjà le cas via `cvb_camel`), et vérifier qu'ACF n'auto-suffixe pas. Tester `graphql` introspection après registration.\n   - **Piège front matching** : avec 90 `name`, le `registryKeys.find(endsWith)` casse (faux positifs). → voir front (ordre 2).\n3. **`scripts/wp-seed/lib.php`** — les pages seedées utilisent `\"layout\": \"hero\"`. Si on renomme les layouts par design, le seed doit mapper `layout`→ design (ou conserver les 26 layouts schema **en plus** des 90 façades). **Recommandation pour préserver la parité sans toucher au seed** : garder les 26 layouts schema existants pour le seed/parité, ET ajouter les 90 façades comme **alias** pointant vers le même `acf_fc_layout` n'est pas possible (clé unique). → Solution : faire pointer le seed sur les nouveaux `name` de design (mettre à jour les `\"layout\"` des 35 fichiers `wordpress/seed/content/*.json` via script). C'est le **plus gros poste**.\n\n**Front (ordre 2)** :\n4. **`src/components/blocks/BlockRenderer.tsx`** — changer le fallback : au lieu de `registryKeys.find((k) => typename.endsWith(k))`, lire **`section.schema`** (nouveau champ) et matcher `registry[pascal(section.schema) + \"Layout\"]`. `origine` reste prioritaire. Cela découple nom-de-layout et composant générique → 90 layouts OK.\n5. **`src/lib/wp/types.ts` + fragments** — ajouter `schema?: string` au type section et au fragment GraphQL de chaque layout (les fragments sélectionnent déjà `origine` ; ajouter `schema`). Régénérer/valider les fragments.\n6. **`src/components/origine/oneoff.tsx`** — écrire les adaptateurs manquants (`LocalHero`, `SolutionSection`, `LocalSolutionSection`, `SectorHero`, `RoofHero`) sur le modèle `makeProductHero` (data-driven + defaults). Enregistrer leurs clés dans `origineRegistry`.\n\n**Vignettes (ordre 3)** :\n7. **`scripts/block-thumbnails/capture.mjs`** — clé de capture = `origine` (et `schema` pour les génériques) ; cible = liste depuis `designs.ts`.\n8. **`scripts/block-thumbnails/capture-gallery.mjs`** — étendre pour rendre une galerie des designs génériques sans page réelle.\n\n**Pièges transverses** :\n- **Parité pixel des pages existantes** : tant que `origine` reste prioritaire dans `BlockRenderer` et que les seeds gardent leur `origine`, les pages produit/industrie/home **ne changent pas d'un pixel**. Ne JAMAIS retirer `origine` du seed.\n- **WPGraphQL** : 90 types de layout → introspection plus lourde, build Next plus long ; vérifier qu'aucun `graphql_field_name` de sous-champ n'entre en collision (tester après chaque batch de 10 designs). C'est la raison pour laquelle (b″) garde les sous-champs **strictement identiques** par schema.\n- **ACF clé/`name` unique** : les 90 `name` de layout doivent être uniques et stables (les changer = perdre le contenu saisi). Geler la convention `name = camelCase(origine)`.\n- **`origine` readonly + default_value** : vérifier qu'ACFE applique bien `default_value` sur un champ `readonly` à la création d'un nouveau row (sinon mettre `readonly` côté JS uniquement, ou poser la valeur via `acf/load_value`). À tester en BO.\n- **Designs « riches figés » (`CoolRoofGuide`, `*Editorial`)** : documenter qu'un nouveau block rend la version data-driven (pas le JSX riche), seul le contenu seed déclenche le rendu riche.\n\n**Ordre d'exécution conseillé** : (1) `designs.php` + `designs.ts` registre → (2) front `BlockRenderer`/`types`/fragments (matching par `schema`, non destructif) → (3) `helpers.php` (champ `schema`) → (4) `sections.php` (génération des 90 layouts) → (5) adaptateurs `oneoff` manquants → (6) migration des `\"layout\"` dans les seeds → (7) régénérer les vignettes → (8) introspection GraphQL + smoke test parité sur 1 page de chaque CPT.\n\n---\n\n## Fichiers clés (chemins absolus)\n\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/blocks/BlockRenderer.tsx` — aiguillage `origine` puis fallback (à passer en matching par `schema`).\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/index.ts` + `home.tsx` / `produits.tsx` / `industrie.tsx` / `oneoff.tsx` — registry des composants bespoke ; `oneoff.tsx` est VIDE (à remplir).\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/sections.php` — registration du flexible content + modale ACFE.\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/helpers.php` — `cvb_layout()` ajoute déjà `origine` readonly ; y ajouter `schema` + `default_value` d'`origine`.\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/layouts/*.php` — 26 field-sets par schema (à réutiliser, pas à dupliquer).\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/seed/lib.php` + `/wordpress/seed/content/*.json` — seed ; `\"layout\"` à migrer vers les noms de design.\n- `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/scripts/block-thumbnails/capture.mjs` + `capture-gallery.mjs` — capture vignettes (clé à passer de `layout` à `origine`).\n- À CRÉER : `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/wordpress/mu-plugins/covalba-core/inc/field-groups/designs.php` et `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/origine/designs.ts` (registre unique des 90 designs).\n\nSnippet load-bearing — l'adaptateur factory est DÉJÀ data-driven (donc éditable sans réécriture), ex. `makeProductHero` dans `produits.tsx` l.245-280 : `tag={s.eyebrow ?? d.tag}`, `productName={s.titre ?? d.productName}`, etc. → poser `origine` suffit à rendre le design éditable.\n\nLe seul changement front structurellement nécessaire est dans `BlockRenderer.tsx` : remplacer\n`const key = registryKeys.find((k) => typename.endsWith(k));`\npar un matching sur un nouveau champ `section.schema` (`registry[pascalCase(section.schema) + \"Layout\"]`), pour découpler le nom du layout (désormais 1 par design) du composant générique (toujours 1 par schema).",
    "designCatalog": "I now have a comprehensive picture of all hero variants and the showcase/solution/grid families. Let me do a final quick check on the IndustrieHero header text and confirm there's no separate \"showcase\" family beyond ProductShowcase. I have enough to write the catalog.\n\nHere is the catalog.\n\n---\n\n# Catalogue des composants de DESIGN distincts — Covalba\n\nRegroupé par famille de section. Pour chaque composant : structure visuelle distinctive en 1 ligne. Tous les chemins sont absolus.\n\n## Famille HÉROS\n\n7 designs réellement distincts (3 archétypes de layout). À noter : `LogistiqueHero`, `DistributionHero` et le `SectorHero` interne à `SectorPageTemplate` sont **visuellement identiques** (même archétype \"photo split + lead form + bandeau logos\"), seules les data/champs de formulaire changent.\n\n- **Hero (home)** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/Hero.tsx`\n  Photo plein écran (`min-h-dvh`), split 55/45 : titre+lead à gauche / carte formulaire \"estimation budget\" double-bezel à droite, **marquee logos animé** en bas (desktop) + grille 6 logos inline (mobile).\n\n- **LocalHero** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/LocalHero.tsx`\n  Même photo plein écran que Hero mais **sans formulaire** : colonne unique gauche (titre+lead) + 2 boutons CTA (primary/secondary pill), marquee logos en bas. Plus épuré.\n\n- **ProductHero** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/ProductHero.tsx`\n  Archétype **bicolore split-screen** (pas de photo full-bleed) : moitié gauche crème (breadcrumb + tag pill coloré + énorme nom produit + barre d'accent verticale colorée + tagline), moitié droite navy avec photo + barre de stats. Couleur d'accent paramétrable par produit.\n\n- **DistributionHero** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/industries/DistributionHero.tsx`\n  Archétype \"photo split + lead form\" : photo art-directed full-bleed navy, split 55/45 (eyebrow teal + H1 + lead + liste de 3 stats à gauche / carte formulaire à droite), **bandeau de 4 logos en grille statique** en bas (pas de marquee).\n\n- **LogistiqueHero** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/industries/LogistiqueHero.tsx`\n  Quasi identique à DistributionHero (même split 55/45 + lead form + stats), seule différence visuelle : le bandeau du bas est un **marquee animé** de 12 logos (comme la home) au lieu d'une grille statique.\n\n- **SectorHero** (interne) — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/industries/SectorPageTemplate.tsx` (l.267)\n  Même design que DistributionHero (photo split + lead form + bandeau logos statique), mais **formulaire à 5 champs** (type bâtiment / climat / surface / année / email) au lieu de 3. Utilisé par Agricole, Collectivités, ERP, Tertiaire.\n\n- **IndustrieHero** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/industrie/IndustrieHero.tsx`\n  Reprend l'archétype split bicolore de ProductHero (58/42 crème-gauche / navy-droite avec photo) mais **sans stats** ; ajoute une barre de \"cartes secteurs\" flottante en débordement bas (`translate-y-[30%]`) chevauchant la section suivante. Layout unique.\n\n- **RoofHero** (interne) — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/roof/RoofPageTemplate.tsx` (l.274)\n  Même base que DistributionHero (photo full-bleed navy + split 55/45 + lead form 4 champs dont un champ readonly affichant le type de toiture), mais bandeau logos en bas = **rangée flex justify-between** (ni grille ni marquee). Utilisé par les pages toitures (BacAcier, Fibrociment, etc.).\n\n## Famille SHOWCASE\n\nUn seul design dans cette famille (pas de doublon).\n\n- **ProductShowcase** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/product/ProductShowcase.tsx`\n  Fond navy, **toggle pills** pour switcher entre variantes (CovaTherm 8/20), carte 2 colonnes : packshot produit débordant sur fond bleu clair à droite + bloc texte blanc (tagline + nom + description + CTA) à gauche, inversion ordre mobile/desktop.\n\n## Famille SOLUTION\n\n4 designs distincts.\n\n- **SolutionSection (home)** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/SolutionSection.tsx`\n  Fond blanc, header centré + **3 cartes blanches** ombre douce alignées (grand chiffre + label + description), gradients radiaux teal ambiants. Pas d'image.\n\n- **LocalSolutionSection** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/LocalSolutionSection.tsx`\n  Fond crème, **split 7/5** : colonne texte à gauche (titre + paragraphe + 3 points avec séparateurs top-border + CTA) / une grande image arrondie à droite. Asymétrique.\n\n- **DistributionSolution** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/industries/DistributionSolution.tsx`\n  Fond bleu nuit (#142333) texturé, header asymétrique, puis **2 cartes comparatives \"avant/après\"** (toiture classique vs cool roof, image + badge tone chaud/froid + métrique encadrée + checklist), suivies d'une rangée de 4 key-points et d'un texte de transition.\n\n- **LogistiqueSolution** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/industries/LogistiqueSolution.tsx`\n  **Visuellement identique** à DistributionSolution (mêmes 2 cartes comparatives, mêmes tons), seules variations : images art-directed (mobile/desktop), rangée de **3 key-points** (au lieu de 4) et pas de texte de transition final. (Le `SolutionSection` interne à `SectorPageTemplate`, l.572, est le même design encore.)\n\n## Famille GRILLE\n\n3 designs distincts.\n\n- **SolutionsGrid (produits)** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/SolutionsGrid.tsx`\n  Fond offwhite, **grille 4 colonnes de cartes produit** à fond pastel coloré (vignette carrée centrée + nom + tagline + description + supports + lien \"En savoir plus\"). Cartes hover-lift.\n\n- **SectorsGrid (secteurs)** — `/opt/projects/.covalba-wp-worktrees/tom-passage-wp/src/components/SectorsGrid.tsx`\n  Fond crème, **grille 3 colonnes de cartes-photo immersives** (image plein cadre 4:3 + overlay navy + flèche ronde en haut-droite + titre/accroche en overlay bas) ; **dernière carte au design distinct** (fond #192A3A + blobs SVG teal, pas de photo) pour \"Devenir applicateur\".\n\n- **SolutionSection (home)** — déjà listée ci-dessus dans la famille SOLUTION ; à noter qu'elle joue aussi le rôle de \"grille 3 cartes chiffres\", mais sans images ni liens (différent des deux grilles ci-dessus).\n\n---\n\n## Notes de cohérence (doublons à connaître)\n\n- Heroes \"photo split + lead form\" : `DistributionHero`, `LogistiqueHero`, `SectorHero` (interne SectorPageTemplate) et `RoofHero` (interne RoofPageTemplate) partagent le **même archétype** ; différences = type de bandeau logos bas (grille statique / marquee / flex-between) et nombre de champs du formulaire (3, 4 ou 5).\n- Heroes \"split bicolore crème/navy\" : `ProductHero` et `IndustrieHero` partagent le **même archétype** ; `IndustrieHero` n'a pas de stats et ajoute une barre de cartes flottante en débordement.\n- Cartes comparatives \"avant/après\" : `DistributionSolution`, `LogistiqueSolution` et le `SolutionSection` interne de `SectorPageTemplate` sont **le même design** (3 implémentations).\n- Les pages secteurs \"génériques\" (Agricole, Collectivités, ERP, Tertiaire) passent toutes par `SectorPageTemplate.tsx`, qui contient en interne SA PROPRE version de chaque famille (hero, problem, solution, durability, benefits, applications, method, proof, faq, finalCta, seo) — ces variantes ne sont pas des fichiers séparés mais des sous-composants du même fichier."
  }
}