{"parentUuid":null,"isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":"Project: Covalba EN website (Vite+React18+TS+Tailwind+shadcn/ui). Repo: /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all . Alias @/=src/.\n\nTASK: Create an ENGLISH variant of the shared roof-type page template. READ src/components/roof/RoofPageTemplate.tsx in full. Then CREATE src/components/en/roof/RoofPageTemplateEN.tsx as a FAITHFUL COPY — identical JSX structure, Tailwind classes, image/icon imports, state/logic, animations, interactions — but with every HARDCODED French user-facing string translated to English.\n\nRules:\n- This template is config-driven via a `page` prop (type RoofPageData). DO NOT translate values coming from `page.*` (those are supplied per-page, already English). ONLY translate the template's OWN hardcoded chrome: section eyebrows/headings, button/CTA labels, breadcrumb, form labels/placeholders, any literal French strings in the JSX, alt texts, \"Sources\"/\"FAQ\"-type labels, success/error messages.\n- Reuse the SAME `RoofPageData` TYPE: import it from the original — `import RoofPageTemplate, { ... }` is wrong; instead `import { type RoofPageData } from '@/data/roofPages'` (check the real export name in src/data/roofPages.ts and use it). Export default `RoofPageTemplateEN` taking the same props shape as the FR template (e.g. `{ page }: { page: RoofPageData }`).\n- Use the EN chrome components: import NavbarEN from '@/components/en/NavbarEN', FooterEN from '@/components/en/FooterEN', StickyMobileCTAEN from '@/components/en/StickyMobileCTAEN' instead of the French Navbar/Footer/StickyMobileCTA, IF the FR template renders them. If the FR template's Seo/usePageMeta derives canonical from page.canonical, keep that (the EN page data will carry the EN canonical).\n- US English. Keep metric units (°C, m², %). Product pivot: CTAs sell the coating (\"Request a quote\"/\"Order a sample\"); applicator = the buyer's own professional contractor, never a recruit.\n- Strip any France-specific hardcoded chrome (CEE, décret, \"fabricant français\", +33, Orléans) — replace with neutral English or omit.\n- Internal links: route hardcoded template links to live EN routes — products → /en/solutions/{covatherm,covaseal-20,covametal-20,covatherm-light}, anything else → /en. Never break the build or leave a dead link.\n- If the template imports French display data from src/data/* for an embedded block that would inject French, make it omittable/neutral (render nothing rather than French) and flag it.\n- Do NOT modify any French file. Only CREATE src/components/en/roof/RoofPageTemplateEN.tsx. Do NOT run npm/tsc/build/git.\n\nReport: the file created, the list of French chrome strings translated (grouped), the exact RoofPageData import you used, and flag anything (data-driven French you neutralized, links, config fields left untouched)."},"uuid":"4a633e35-674a-4e83-8c62-2ab319bbb5ee","timestamp":"2026-06-22T00:03:19.941Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"4a633e35-674a-4e83-8c62-2ab319bbb5ee","isSidechain":true,"agentId":"acb7cb0381e40f6e1","attachment":{"type":"deferred_tools_delta","addedNames":["CronCreate","CronDelete","CronList","DesignSync","EnterWorktree","ExitWorktree","ListMcpResourcesTool","Monitor","NotebookEdit","PushNotification","ReadMcpResourceTool","RemoteTrigger","TaskCreate","TaskGet","TaskList","TaskStop","TaskUpdate","WebFetch","WebSearch","mcp__EstimanouContent__add_article_block","mcp__EstimanouContent__add_block","mcp__EstimanouContent__create_article","mcp__EstimanouContent__create_page","mcp__EstimanouContent__delete_article","mcp__EstimanouContent__delete_article_block","mcp__EstimanouContent__delete_block","mcp__EstimanouContent__get_article","mcp__EstimanouContent__get_city_page","mcp__EstimanouContent__get_page","mcp__EstimanouContent__list_articles","mcp__EstimanouContent__list_available_blocks","mcp__EstimanouContent__list_city_pages","mcp__EstimanouContent__list_pages","mcp__EstimanouContent__list_site_urls","mcp__EstimanouContent__reorder_article_blocks","mcp__EstimanouContent__reorder_blocks","mcp__EstimanouContent__replace_article_content","mcp__EstimanouContent__replace_city_seo_content","mcp__EstimanouContent__replace_page_layout","mcp__EstimanouContent__search_internal_links","mcp__EstimanouContent__update_article","mcp__EstimanouContent__update_article_block","mcp__EstimanouContent__update_block","mcp__EstimanouContent__update_city_page_content","mcp__claude_ai_Ahref__batch-analysis","mcp__claude_ai_Ahref__brand-radar-ai-responses","mcp__claude_ai_Ahref__brand-radar-ai-responses-entities","mcp__claude_ai_Ahref__brand-radar-cited-domains","mcp__claude_ai_Ahref__brand-radar-cited-domains-entities","mcp__claude_ai_Ahref__brand-radar-cited-pages","mcp__claude_ai_Ahref__brand-radar-cited-pages-entities","mcp__claude_ai_Ahref__brand-radar-impressions-history","mcp__claude_ai_Ahref__brand-radar-impressions-overview","mcp__claude_ai_Ahref__brand-radar-impressions-overview-entities","mcp__claude_ai_Ahref__brand-radar-mentions-history","mcp__claude_ai_Ahref__brand-radar-mentions-history-entities","mcp__claude_ai_Ahref__brand-radar-mentions-overview","mcp__claude_ai_Ahref__brand-radar-mentions-overview-entities","mcp__claude_ai_Ahref__brand-radar-sov-history","mcp__claude_ai_Ahref__brand-radar-sov-history-entities","mcp__claude_ai_Ahref__brand-radar-sov-overview","mcp__claude_ai_Ahref__brand-radar-sov-overview-entities","mcp__claude_ai_Ahref__doc","mcp__claude_ai_Ahref__gsc-anonymous-queries","mcp__claude_ai_Ahref__gsc-ctr-by-position","mcp__claude_ai_Ahref__gsc-keyword-history","mcp__claude_ai_Ahref__gsc-keywords","mcp__claude_ai_Ahref__gsc-metrics-by-country","mcp__claude_ai_Ahref__gsc-page-history","mcp__claude_ai_Ahref__gsc-pages","mcp__claude_ai_Ahref__gsc-pages-history","mcp__claude_ai_Ahref__gsc-performance-by-device","mcp__claude_ai_Ahref__gsc-performance-by-position","mcp__claude_ai_Ahref__gsc-performance-history","mcp__claude_ai_Ahref__gsc-positions-history","mcp__claude_ai_Ahref__keywords-explorer-matching-terms","mcp__claude_ai_Ahref__keywords-explorer-overview","mcp__claude_ai_Ahref__keywords-explorer-related-terms","mcp__claude_ai_Ahref__keywords-explorer-search-suggestions","mcp__claude_ai_Ahref__keywords-explorer-volume-by-country","mcp__claude_ai_Ahref__keywords-explorer-volume-history","mcp__claude_ai_Ahref__management-brand-radar-prompts","mcp__claude_ai_Ahref__management-brand-radar-reports","mcp__claude_ai_Ahref__management-keyword-list-keywords","mcp__claude_ai_Ahref__management-locations","mcp__claude_ai_Ahref__management-project-competitors","mcp__claude_ai_Ahref__management-project-keywords","mcp__claude_ai_Ahref__management-projects","mcp__claude_ai_Ahref__public-crawler-ip-ranges","mcp__claude_ai_Ahref__public-crawler-ips","mcp__claude_ai_Ahref__public-domain-rating-free","mcp__claude_ai_Ahref__rank-tracker-competitors-domains","mcp__claude_ai_Ahref__rank-tracker-competitors-overview","mcp__claude_ai_Ahref__rank-tracker-competitors-pages","mcp__claude_ai_Ahref__rank-tracker-competitors-stats","mcp__claude_ai_Ahref__rank-tracker-overview","mcp__claude_ai_Ahref__rank-tracker-serp-overview","mcp__claude_ai_Ahref__render-data-table","mcp__claude_ai_Ahref__render-scorecard","mcp__claude_ai_Ahref__render-time-series-chart","mcp__claude_ai_Ahref__serp-overview","mcp__claude_ai_Ahref__site-audit-issues","mcp__claude_ai_Ahref__site-audit-page-content","mcp__claude_ai_Ahref__site-audit-page-explorer","mcp__claude_ai_Ahref__site-audit-projects","mcp__claude_ai_Ahref__site-explorer-all-backlinks","mcp__claude_ai_Ahref__site-explorer-anchors","mcp__claude_ai_Ahref__site-explorer-backlinks-stats","mcp__claude_ai_Ahref__site-explorer-broken-backlinks","mcp__claude_ai_Ahref__site-explorer-crawled-pages","mcp__claude_ai_Ahref__site-explorer-domain-rating","mcp__claude_ai_Ahref__site-explorer-domain-rating-history","mcp__claude_ai_Ahref__site-explorer-keywords-history","mcp__claude_ai_Ahref__site-explorer-linked-anchors-external","mcp__claude_ai_Ahref__site-explorer-linked-anchors-internal","mcp__claude_ai_Ahref__site-explorer-linked-domains","mcp__claude_ai_Ahref__site-explorer-metrics","mcp__claude_ai_Ahref__site-explorer-metrics-by-country","mcp__claude_ai_Ahref__site-explorer-metrics-history","mcp__claude_ai_Ahref__site-explorer-organic-competitors","mcp__claude_ai_Ahref__site-explorer-organic-keywords","mcp__claude_ai_Ahref__site-explorer-outlinks-stats","mcp__claude_ai_Ahref__site-explorer-pages-by-backlinks","mcp__claude_ai_Ahref__site-explorer-pages-by-internal-links","mcp__claude_ai_Ahref__site-explorer-pages-by-traffic","mcp__claude_ai_Ahref__site-explorer-pages-history","mcp__claude_ai_Ahref__site-explorer-paid-pages","mcp__claude_ai_Ahref__site-explorer-refdomains-history","mcp__claude_ai_Ahref__site-explorer-referring-domains","mcp__claude_ai_Ahref__site-explorer-top-pages","mcp__claude_ai_Ahref__site-explorer-total-search-volume-history","mcp__claude_ai_Ahref__site-explorer-url-rating-history","mcp__claude_ai_Ahref__social-media-activity-history","mcp__claude_ai_Ahref__social-media-authors","mcp__claude_ai_Ahref__social-media-channel-metrics","mcp__claude_ai_Ahref__social-media-channels","mcp__claude_ai_Ahref__social-media-post-metrics","mcp__claude_ai_Ahref__social-media-posts","mcp__claude_ai_Ahref__subscription-info-limits-and-usage","mcp__claude_ai_Ahref__web-analytics-browser-versions","mcp__claude_ai_Ahref__web-analytics-browser-versions-chart","mcp__claude_ai_Ahref__web-analytics-browsers","mcp__claude_ai_Ahref__web-analytics-browsers-chart","mcp__claude_ai_Ahref__web-analytics-chart","mcp__claude_ai_Ahref__web-analytics-cities","mcp__claude_ai_Ahref__web-analytics-cities-chart","mcp__claude_ai_Ahref__web-analytics-continents","mcp__claude_ai_Ahref__web-analytics-continents-chart","mcp__claude_ai_Ahref__web-analytics-countries","mcp__claude_ai_Ahref__web-analytics-countries-chart","mcp__claude_ai_Ahref__web-analytics-devices","mcp__claude_ai_Ahref__web-analytics-devices-chart","mcp__claude_ai_Ahref__web-analytics-entry-pages","mcp__claude_ai_Ahref__web-analytics-entry-pages-chart","mcp__claude_ai_Ahref__web-analytics-exit-pages","mcp__claude_ai_Ahref__web-analytics-exit-pages-chart","mcp__claude_ai_Ahref__web-analytics-languages","mcp__claude_ai_Ahref__web-analytics-languages-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems","mcp__claude_ai_Ahref__web-analytics-operating-systems-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions-chart","mcp__claude_ai_Ahref__web-analytics-referrers","mcp__claude_ai_Ahref__web-analytics-referrers-chart","mcp__claude_ai_Ahref__web-analytics-source-channels","mcp__claude_ai_Ahref__web-analytics-source-channels-chart","mcp__claude_ai_Ahref__web-analytics-sources","mcp__claude_ai_Ahref__web-analytics-sources-chart","mcp__claude_ai_Ahref__web-analytics-stats","mcp__claude_ai_Ahref__web-analytics-top-pages","mcp__claude_ai_Ahref__web-analytics-top-pages-chart","mcp__claude_ai_Ahref__web-analytics-utm-params","mcp__claude_ai_Ahref__web-analytics-utm-params-chart","mcp__claude_ai_Canva__cancel-editing-transaction","mcp__claude_ai_Canva__comment-on-design","mcp__claude_ai_Canva__commit-editing-transaction","mcp__claude_ai_Canva__copy-design","mcp__claude_ai_Canva__create-brand-template-draft","mcp__claude_ai_Canva__create-design-from-brand-template","mcp__claude_ai_Canva__create-design-from-candidate","mcp__claude_ai_Canva__create-folder","mcp__claude_ai_Canva__export-design","mcp__claude_ai_Canva__generate-design","mcp__claude_ai_Canva__generate-design-structured","mcp__claude_ai_Canva__get-assets","mcp__claude_ai_Canva__get-brand-template-dataset","mcp__claude_ai_Canva__get-design","mcp__claude_ai_Canva__get-design-candidates","mcp__claude_ai_Canva__get-design-content","mcp__claude_ai_Canva__get-design-pages","mcp__claude_ai_Canva__get-design-thumbnail","mcp__claude_ai_Canva__get-export-formats","mcp__claude_ai_Canva__get-presenter-notes","mcp__claude_ai_Canva__help","mcp__claude_ai_Canva__import-design-from-url","mcp__claude_ai_Canva__list-brand-kits","mcp__claude_ai_Canva__list-comments","mcp__claude_ai_Canva__list-folder-items","mcp__claude_ai_Canva__list-replies","mcp__claude_ai_Canva__merge-designs","mcp__claude_ai_Canva__move-item-to-folder","mcp__claude_ai_Canva__perform-editing-operations","mcp__claude_ai_Canva__publish-brand-template","mcp__claude_ai_Canva__reply-to-comment","mcp__claude_ai_Canva__request-outline-review","mcp__claude_ai_Canva__resize-design","mcp__claude_ai_Canva__resolve-shortlink","mcp__claude_ai_Canva__search-brand-templates","mcp__claude_ai_Canva__search-designs","mcp__claude_ai_Canva__search-folders","mcp__claude_ai_Canva__start-editing-transaction","mcp__claude_ai_Canva__upload-asset-from-url","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_info","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_openapi_spec","mcp__claude_ai_DATA_GOUV_FR__get_dataset_info","mcp__claude_ai_DATA_GOUV_FR__get_metrics","mcp__claude_ai_DATA_GOUV_FR__get_resource_info","mcp__claude_ai_DATA_GOUV_FR__list_dataset_resources","mcp__claude_ai_DATA_GOUV_FR__query_resource_data","mcp__claude_ai_DATA_GOUV_FR__search_dataservices","mcp__claude_ai_DATA_GOUV_FR__search_datasets","mcp__claude_ai_DATA_GOUV_FR__search_organizations","mcp__claude_ai_Data_For_SEO__authenticate","mcp__claude_ai_Data_For_SEO__complete_authentication","mcp__claude_ai_Esteworld-Body__add_block","mcp__claude_ai_Esteworld-Body__delete_block","mcp__claude_ai_Esteworld-Body__find_esteworld_page","mcp__claude_ai_Esteworld-Body__fix_global_blocks","mcp__claude_ai_Esteworld-Body__get_block","mcp__claude_ai_Esteworld-Body__get_blocks_library","mcp__claude_ai_Esteworld-Body__get_breadcrumb","mcp__claude_ai_Esteworld-Body__get_fr_en_mapping","mcp__claude_ai_Esteworld-Body__get_internal_links","mcp__claude_ai_Esteworld-Body__get_internal_links_en","mcp__claude_ai_Esteworld-Body__get_maillage_rules","mcp__claude_ai_Esteworld-Body__get_page","mcp__claude_ai_Esteworld-Body__get_page_json","mcp__claude_ai_Esteworld-Body__get_page_status","mcp__claude_ai_Esteworld-Body__get_redirections","mcp__claude_ai_Esteworld-Body__get_section_content","mcp__claude_ai_Esteworld-Body__get_thematique_pages","mcp__claude_ai_Esteworld-Body__get_translation_languages","mcp__claude_ai_Esteworld-Body__list_pages","mcp__claude_ai_Esteworld-Body__list_pages_by_thematique","mcp__claude_ai_Esteworld-Body__list_pages_by_type","mcp__claude_ai_Esteworld-Body__list_pages_to_create","mcp__claude_ai_Esteworld-Body__list_pages_to_rewrite","mcp__claude_ai_Esteworld-Body__list_pages_with_esteworld","mcp__claude_ai_Esteworld-Body__list_pages_without_esteworld","mcp__claude_ai_Esteworld-Body__n8n_activate_workflow","mcp__claude_ai_Esteworld-Body__n8n_get_executions","mcp__claude_ai_Esteworld-Body__n8n_get_workflow","mcp__claude_ai_Esteworld-Body__n8n_list_workflows","mcp__claude_ai_Esteworld-Body__n8n_trigger_webhook","mcp__claude_ai_Esteworld-Body__preview_global_fixes","mcp__claude_ai_Esteworld-Body__reorder_blocks","mcp__claude_ai_Esteworld-Body__search_content","mcp__claude_ai_Esteworld-Body__search_links_by_keyword","mcp__claude_ai_Esteworld-Body__search_links_by_keyword_en","mcp__claude_ai_Esteworld-Body__submit_page","mcp__claude_ai_Esteworld-Body__submit_page_light","mcp__claude_ai_Esteworld-Body__translate_page","mcp__claude_ai_Esteworld-Body__update_block","mcp__claude_ai_Figma__add_code_connect_map","mcp__claude_ai_Figma__create_new_file","mcp__claude_ai_Figma__download_assets","mcp__claude_ai_Figma__generate_diagram","mcp__claude_ai_Figma__get_code_connect_map","mcp__claude_ai_Figma__get_code_connect_suggestions","mcp__claude_ai_Figma__get_context_for_code_connect","mcp__claude_ai_Figma__get_design_context","mcp__claude_ai_Figma__get_figjam","mcp__claude_ai_Figma__get_libraries","mcp__claude_ai_Figma__get_metadata","mcp__claude_ai_Figma__get_screenshot","mcp__claude_ai_Figma__get_variable_defs","mcp__claude_ai_Figma__search_design_system","mcp__claude_ai_Figma__send_code_connect_mappings","mcp__claude_ai_Figma__upload_assets","mcp__claude_ai_Figma__use_figma","mcp__claude_ai_Figma__whoami","mcp__claude_ai_Gmail__create_draft","mcp__claude_ai_Gmail__create_label","mcp__claude_ai_Gmail__delete_label","mcp__claude_ai_Gmail__get_thread","mcp__claude_ai_Gmail__label_message","mcp__claude_ai_Gmail__label_thread","mcp__claude_ai_Gmail__list_drafts","mcp__claude_ai_Gmail__list_labels","mcp__claude_ai_Gmail__search_threads","mcp__claude_ai_Gmail__unlabel_message","mcp__claude_ai_Gmail__unlabel_thread","mcp__claude_ai_Gmail__update_label","mcp__claude_ai_Google_Calendar__create_event","mcp__claude_ai_Google_Calendar__delete_event","mcp__claude_ai_Google_Calendar__get_event","mcp__claude_ai_Google_Calendar__list_calendars","mcp__claude_ai_Google_Calendar__list_events","mcp__claude_ai_Google_Calendar__respond_to_event","mcp__claude_ai_Google_Calendar__suggest_time","mcp__claude_ai_Google_Calendar__update_event","mcp__claude_ai_Google_Drive__copy_file","mcp__claude_ai_Google_Drive__create_file","mcp__claude_ai_Google_Drive__download_file_content","mcp__claude_ai_Google_Drive__get_file_metadata","mcp__claude_ai_Google_Drive__get_file_permissions","mcp__claude_ai_Google_Drive__list_recent_files","mcp__claude_ai_Google_Drive__read_file_content","mcp__claude_ai_Google_Drive__search_files","mcp__claude_ai_SEMRush__backlink_research","mcp__claude_ai_SEMRush__execute_report","mcp__claude_ai_SEMRush__get_report_schema","mcp__claude_ai_SEMRush__keyword_research","mcp__claude_ai_SEMRush__organic_research","mcp__claude_ai_SEMRush__overview_research","mcp__claude_ai_SEMRush__projects_research","mcp__claude_ai_SEMRush__siteaudit_research","mcp__claude_ai_SEMRush__subdomain_research","mcp__claude_ai_SEMRush__subfolder_research","mcp__claude_ai_SEMRush__tracking_research","mcp__claude_ai_SEMRush__trends_research","mcp__claude_ai_SEMRush__url_research","mcp__claude_ai_Vercel__add_toolbar_reaction","mcp__claude_ai_Vercel__change_toolbar_thread_resolve_status","mcp__claude_ai_Vercel__check_domain_availability_and_price","mcp__claude_ai_Vercel__deploy_to_vercel","mcp__claude_ai_Vercel__edit_toolbar_message","mcp__claude_ai_Vercel__get_access_to_vercel_url","mcp__claude_ai_Vercel__get_deployment","mcp__claude_ai_Vercel__get_deployment_build_logs","mcp__claude_ai_Vercel__get_project","mcp__claude_ai_Vercel__get_runtime_logs","mcp__claude_ai_Vercel__get_toolbar_thread","mcp__claude_ai_Vercel__import-claude-design-from-url","mcp__claude_ai_Vercel__list_deployments","mcp__claude_ai_Vercel__list_projects","mcp__claude_ai_Vercel__list_teams","mcp__claude_ai_Vercel__list_toolbar_threads","mcp__claude_ai_Vercel__reply_to_toolbar_thread","mcp__claude_ai_Vercel__search_vercel_documentation","mcp__claude_ai_Vercel__web_fetch_vercel_url","mcp__gpt-image-2__continue_edit_session","mcp__gpt-image-2__edit_image","mcp__gpt-image-2__end_edit_session","mcp__gpt-image-2__generate_image","mcp__gpt-image-2__list_edit_sessions","mcp__gpt-image-2__start_edit_session","mcp__hostinger__DNS_deleteDNSRecordsV1","mcp__hostinger__DNS_getDNSRecordsV1","mcp__hostinger__DNS_getDNSSnapshotListV1","mcp__hostinger__DNS_getDNSSnapshotV1","mcp__hostinger__DNS_resetDNSRecordsV1","mcp__hostinger__DNS_restoreDNSSnapshotV1","mcp__hostinger__DNS_updateDNSRecordsV1","mcp__hostinger__DNS_validateDNSRecordsV1","mcp__hostinger__VPS_activateFirewallV1","mcp__hostinger__VPS_attachPublicKeyV1","mcp__hostinger__VPS_createFirewallRuleV1","mcp__hostinger__VPS_createNewFirewallV1","mcp__hostinger__VPS_createNewProjectV1","mcp__hostinger__VPS_createPTRRecordV1","mcp__hostinger__VPS_createPostInstallScriptV1","mcp__hostinger__VPS_createPublicKeyV1","mcp__hostinger__VPS_createSnapshotV1","mcp__hostinger__VPS_deactivateFirewallV1","mcp__hostinger__VPS_deleteFirewallRuleV1","mcp__hostinger__VPS_deleteFirewallV1","mcp__hostinger__VPS_deletePTRRecordV1","mcp__hostinger__VPS_deletePostInstallScriptV1","mcp__hostinger__VPS_deleteProjectV1","mcp__hostinger__VPS_deletePublicKeyV1","mcp__hostinger__VPS_deleteSnapshotV1","mcp__hostinger__VPS_getActionDetailsV1","mcp__hostinger__VPS_getActionsV1","mcp__hostinger__VPS_getAttachedPublicKeysV1","mcp__hostinger__VPS_getBackupsV1","mcp__hostinger__VPS_getDataCenterListV1","mcp__hostinger__VPS_getFirewallDetailsV1","mcp__hostinger__VPS_getFirewallListV1","mcp__hostinger__VPS_getMetricsV1","mcp__hostinger__VPS_getPostInstallScriptV1","mcp__hostinger__VPS_getPostInstallScriptsV1","mcp__hostinger__VPS_getProjectContainersV1","mcp__hostinger__VPS_getProjectContentsV1","mcp__hostinger__VPS_getProjectListV1","mcp__hostinger__VPS_getProjectLogsV1","mcp__hostinger__VPS_getPublicKeysV1","mcp__hostinger__VPS_getScanMetricsV1","mcp__hostinger__VPS_getSnapshotV1","mcp__hostinger__VPS_getTemplateDetailsV1","mcp__hostinger__VPS_getTemplatesV1","mcp__hostinger__VPS_getVirtualMachineDetailsV1","mcp__hostinger__VPS_getVirtualMachinesV1","mcp__hostinger__VPS_installMonarxV1","mcp__hostinger__VPS_purchaseNewVirtualMachineV1","mcp__hostinger__VPS_recreateVirtualMachineV1","mcp__hostinger__VPS_resetHostnameV1","mcp__hostinger__VPS_restartProjectV1","mcp__hostinger__VPS_restartVirtualMachineV1","mcp__hostinger__VPS_restoreBackupV1","mcp__hostinger__VPS_restoreSnapshotV1","mcp__hostinger__VPS_setHostnameV1","mcp__hostinger__VPS_setNameserversV1","mcp__hostinger__VPS_setPanelPasswordV1","mcp__hostinger__VPS_setRootPasswordV1","mcp__hostinger__VPS_setupPurchasedVirtualMachineV1","mcp__hostinger__VPS_startProjectV1","mcp__hostinger__VPS_startRecoveryModeV1","mcp__hostinger__VPS_startVirtualMachineV1","mcp__hostinger__VPS_stopProjectV1","mcp__hostinger__VPS_stopRecoveryModeV1","mcp__hostinger__VPS_stopVirtualMachineV1","mcp__hostinger__VPS_syncFirewallV1","mcp__hostinger__VPS_uninstallMonarxV1","mcp__hostinger__VPS_updateFirewallRuleV1","mcp__hostinger__VPS_updatePostInstallScriptV1","mcp__hostinger__VPS_updateProjectV1","mcp__hostinger__billing_deletePaymentMethodV1","mcp__hostinger__billing_disableAutoRenewalV1","mcp__hostinger__billing_enableAutoRenewalV1","mcp__hostinger__billing_getCatalogItemListV1","mcp__hostinger__billing_getPaymentMethodListV1","mcp__hostinger__billing_getSubscriptionListV1","mcp__hostinger__billing_setDefaultPaymentMethodV1","mcp__hostinger__domains_checkDomainAvailabilityV1","mcp__hostinger__domains_createDomainForwardingV1","mcp__hostinger__domains_createWHOISProfileV1","mcp__hostinger__domains_deleteDomainForwardingV1","mcp__hostinger__domains_deleteWHOISProfileV1","mcp__hostinger__domains_disableDomainLockV1","mcp__hostinger__domains_disablePrivacyProtectionV1","mcp__hostinger__domains_enableDomainLockV1","mcp__hostinger__domains_enablePrivacyProtectionV1","mcp__hostinger__domains_getDomainDetailsV1","mcp__hostinger__domains_getDomainForwardingV1","mcp__hostinger__domains_getDomainListV1","mcp__hostinger__domains_getWHOISProfileListV1","mcp__hostinger__domains_getWHOISProfileUsageV1","mcp__hostinger__domains_getWHOISProfileV1","mcp__hostinger__domains_purchaseNewDomainV1","mcp__hostinger__domains_updateDomainNameserversV1","mcp__hostinger__hosting_createAccountDatabaseV1","mcp__hostinger__hosting_createWebsiteParkedDomainV1","mcp__hostinger__hosting_createWebsiteSubdomainV1","mcp__hostinger__hosting_createWebsiteV1","mcp__hostinger__hosting_deleteAccountDatabaseV1","mcp__hostinger__hosting_deleteWebsiteParkedDomainV1","mcp__hostinger__hosting_deleteWebsiteSubdomainV1","mcp__hostinger__hosting_deployJsApplication","mcp__hostinger__hosting_deployStaticWebsite","mcp__hostinger__hosting_deployWordpressPlugin","mcp__hostinger__hosting_deployWordpressTheme","mcp__hostinger__hosting_generateAFreeSubdomainV1","mcp__hostinger__hosting_importWordpressWebsite","mcp__hostinger__hosting_installWordPressV1","mcp__hostinger__hosting_listAccountDatabasesV1","mcp__hostinger__hosting_listAvailableDatacentersV1","mcp__hostinger__hosting_listJsDeployments","mcp__hostinger__hosting_listOrdersV1","mcp__hostinger__hosting_listWebsiteParkedDomainsV1","mcp__hostinger__hosting_listWebsiteSubdomainsV1","mcp__hostinger__hosting_listWebsitesV1","mcp__hostinger__hosting_listWordPressInstallationsV1","mcp__hostinger__hosting_showJsDeploymentLogs","mcp__hostinger__hosting_verifyDomainOwnershipV1","mcp__hostinger__reach_createANewContactSegmentV1","mcp__hostinger__reach_createANewContactV1","mcp__hostinger__reach_createNewContactsV1","mcp__hostinger__reach_deleteAContactV1","mcp__hostinger__reach_getSegmentDetailsV1","mcp__hostinger__reach_listContactGroupsV1","mcp__hostinger__reach_listContactsV1","mcp__hostinger__reach_listProfilesV1","mcp__hostinger__reach_listSegmentContactsV1","mcp__hostinger__reach_listSegmentsV1","mcp__hostinger__v2_getDomainVerificationsDIRECT","mcp__n8n-mcp__get_node","mcp__n8n-mcp__get_template","mcp__n8n-mcp__n8n_audit_instance","mcp__n8n-mcp__n8n_autofix_workflow","mcp__n8n-mcp__n8n_create_workflow","mcp__n8n-mcp__n8n_delete_workflow","mcp__n8n-mcp__n8n_deploy_template","mcp__n8n-mcp__n8n_executions","mcp__n8n-mcp__n8n_generate_workflow","mcp__n8n-mcp__n8n_get_workflow","mcp__n8n-mcp__n8n_health_check","mcp__n8n-mcp__n8n_list_workflows","mcp__n8n-mcp__n8n_manage_credentials","mcp__n8n-mcp__n8n_manage_datatable","mcp__n8n-mcp__n8n_test_workflow","mcp__n8n-mcp__n8n_update_full_workflow","mcp__n8n-mcp__n8n_update_partial_workflow","mcp__n8n-mcp__n8n_validate_workflow","mcp__n8n-mcp__n8n_workflow_versions","mcp__n8n-mcp__search_nodes","mcp__n8n-mcp__search_templates","mcp__n8n-mcp__tools_documentation","mcp__n8n-mcp__validate_node","mcp__n8n-mcp__validate_workflow","mcp__nanobanana__generate_image","mcp__nanobanana__maintenance","mcp__nanobanana__show_output_stats","mcp__nanobanana__upload_file","mcp__plugin_vercel_vercel__authenticate","mcp__plugin_vercel_vercel__complete_authentication","mcp__refero__refero_get_flow","mcp__refero__refero_get_screen","mcp__refero__refero_get_screen_image","mcp__refero__refero_get_similar_screens","mcp__refero__refero_get_style","mcp__refero__refero_search_flows","mcp__refero__refero_search_screens","mcp__refero__refero_search_styles","mcp__semrush__authenticate","mcp__semrush__complete_authentication","mcp__stock-images-mcp__search_stock_images","mcp__trello__add_card_to_list","mcp__trello__add_checklist_item","mcp__trello__add_comment","mcp__trello__add_list_to_board","mcp__trello__archive_card","mcp__trello__archive_list","mcp__trello__assign_member_to_card","mcp__trello__attach_file_to_card","mcp__trello__attach_image_data_to_card","mcp__trello__attach_image_to_card","mcp__trello__create_board","mcp__trello__create_checklist","mcp__trello__create_label","mcp__trello__delete_comment","mcp__trello__delete_label","mcp__trello__find_checklist_items_by_description","mcp__trello__get_acceptance_criteria","mcp__trello__get_active_board_info","mcp__trello__get_board_labels","mcp__trello__get_board_members","mcp__trello__get_card","mcp__trello__get_card_comments","mcp__trello__get_card_history","mcp__trello__get_cards_by_list_id","mcp__trello__get_checklist_by_name","mcp__trello__get_checklist_items","mcp__trello__get_health","mcp__trello__get_health_detailed","mcp__trello__get_health_metadata","mcp__trello__get_health_performance","mcp__trello__get_lists","mcp__trello__get_my_cards","mcp__trello__get_recent_activity","mcp__trello__list_boards","mcp__trello__list_boards_in_workspace","mcp__trello__list_workspaces","mcp__trello__move_card","mcp__trello__perform_system_repair","mcp__trello__remove_member_from_card","mcp__trello__set_active_board","mcp__trello__set_active_workspace","mcp__trello__update_card_details","mcp__trello__update_checklist_item","mcp__trello__update_comment","mcp__trello__update_label"],"addedLines":["CronCreate","CronDelete","CronList","DesignSync","EnterWorktree","ExitWorktree","ListMcpResourcesTool","Monitor","NotebookEdit","PushNotification","ReadMcpResourceTool","RemoteTrigger","TaskCreate","TaskGet","TaskList","TaskStop","TaskUpdate","WebFetch","WebSearch","mcp__EstimanouContent__add_article_block","mcp__EstimanouContent__add_block","mcp__EstimanouContent__create_article","mcp__EstimanouContent__create_page","mcp__EstimanouContent__delete_article","mcp__EstimanouContent__delete_article_block","mcp__EstimanouContent__delete_block","mcp__EstimanouContent__get_article","mcp__EstimanouContent__get_city_page","mcp__EstimanouContent__get_page","mcp__EstimanouContent__list_articles","mcp__EstimanouContent__list_available_blocks","mcp__EstimanouContent__list_city_pages","mcp__EstimanouContent__list_pages","mcp__EstimanouContent__list_site_urls","mcp__EstimanouContent__reorder_article_blocks","mcp__EstimanouContent__reorder_blocks","mcp__EstimanouContent__replace_article_content","mcp__EstimanouContent__replace_city_seo_content","mcp__EstimanouContent__replace_page_layout","mcp__EstimanouContent__search_internal_links","mcp__EstimanouContent__update_article","mcp__EstimanouContent__update_article_block","mcp__EstimanouContent__update_block","mcp__EstimanouContent__update_city_page_content","mcp__claude_ai_Ahref__batch-analysis","mcp__claude_ai_Ahref__brand-radar-ai-responses","mcp__claude_ai_Ahref__brand-radar-ai-responses-entities","mcp__claude_ai_Ahref__brand-radar-cited-domains","mcp__claude_ai_Ahref__brand-radar-cited-domains-entities","mcp__claude_ai_Ahref__brand-radar-cited-pages","mcp__claude_ai_Ahref__brand-radar-cited-pages-entities","mcp__claude_ai_Ahref__brand-radar-impressions-history","mcp__claude_ai_Ahref__brand-radar-impressions-overview","mcp__claude_ai_Ahref__brand-radar-impressions-overview-entities","mcp__claude_ai_Ahref__brand-radar-mentions-history","mcp__claude_ai_Ahref__brand-radar-mentions-history-entities","mcp__claude_ai_Ahref__brand-radar-mentions-overview","mcp__claude_ai_Ahref__brand-radar-mentions-overview-entities","mcp__claude_ai_Ahref__brand-radar-sov-history","mcp__claude_ai_Ahref__brand-radar-sov-history-entities","mcp__claude_ai_Ahref__brand-radar-sov-overview","mcp__claude_ai_Ahref__brand-radar-sov-overview-entities","mcp__claude_ai_Ahref__doc","mcp__claude_ai_Ahref__gsc-anonymous-queries","mcp__claude_ai_Ahref__gsc-ctr-by-position","mcp__claude_ai_Ahref__gsc-keyword-history","mcp__claude_ai_Ahref__gsc-keywords","mcp__claude_ai_Ahref__gsc-metrics-by-country","mcp__claude_ai_Ahref__gsc-page-history","mcp__claude_ai_Ahref__gsc-pages","mcp__claude_ai_Ahref__gsc-pages-history","mcp__claude_ai_Ahref__gsc-performance-by-device","mcp__claude_ai_Ahref__gsc-performance-by-position","mcp__claude_ai_Ahref__gsc-performance-history","mcp__claude_ai_Ahref__gsc-positions-history","mcp__claude_ai_Ahref__keywords-explorer-matching-terms","mcp__claude_ai_Ahref__keywords-explorer-overview","mcp__claude_ai_Ahref__keywords-explorer-related-terms","mcp__claude_ai_Ahref__keywords-explorer-search-suggestions","mcp__claude_ai_Ahref__keywords-explorer-volume-by-country","mcp__claude_ai_Ahref__keywords-explorer-volume-history","mcp__claude_ai_Ahref__management-brand-radar-prompts","mcp__claude_ai_Ahref__management-brand-radar-reports","mcp__claude_ai_Ahref__management-keyword-list-keywords","mcp__claude_ai_Ahref__management-locations","mcp__claude_ai_Ahref__management-project-competitors","mcp__claude_ai_Ahref__management-project-keywords","mcp__claude_ai_Ahref__management-projects","mcp__claude_ai_Ahref__public-crawler-ip-ranges","mcp__claude_ai_Ahref__public-crawler-ips","mcp__claude_ai_Ahref__public-domain-rating-free","mcp__claude_ai_Ahref__rank-tracker-competitors-domains","mcp__claude_ai_Ahref__rank-tracker-competitors-overview","mcp__claude_ai_Ahref__rank-tracker-competitors-pages","mcp__claude_ai_Ahref__rank-tracker-competitors-stats","mcp__claude_ai_Ahref__rank-tracker-overview","mcp__claude_ai_Ahref__rank-tracker-serp-overview","mcp__claude_ai_Ahref__render-data-table","mcp__claude_ai_Ahref__render-scorecard","mcp__claude_ai_Ahref__render-time-series-chart","mcp__claude_ai_Ahref__serp-overview","mcp__claude_ai_Ahref__site-audit-issues","mcp__claude_ai_Ahref__site-audit-page-content","mcp__claude_ai_Ahref__site-audit-page-explorer","mcp__claude_ai_Ahref__site-audit-projects","mcp__claude_ai_Ahref__site-explorer-all-backlinks","mcp__claude_ai_Ahref__site-explorer-anchors","mcp__claude_ai_Ahref__site-explorer-backlinks-stats","mcp__claude_ai_Ahref__site-explorer-broken-backlinks","mcp__claude_ai_Ahref__site-explorer-crawled-pages","mcp__claude_ai_Ahref__site-explorer-domain-rating","mcp__claude_ai_Ahref__site-explorer-domain-rating-history","mcp__claude_ai_Ahref__site-explorer-keywords-history","mcp__claude_ai_Ahref__site-explorer-linked-anchors-external","mcp__claude_ai_Ahref__site-explorer-linked-anchors-internal","mcp__claude_ai_Ahref__site-explorer-linked-domains","mcp__claude_ai_Ahref__site-explorer-metrics","mcp__claude_ai_Ahref__site-explorer-metrics-by-country","mcp__claude_ai_Ahref__site-explorer-metrics-history","mcp__claude_ai_Ahref__site-explorer-organic-competitors","mcp__claude_ai_Ahref__site-explorer-organic-keywords","mcp__claude_ai_Ahref__site-explorer-outlinks-stats","mcp__claude_ai_Ahref__site-explorer-pages-by-backlinks","mcp__claude_ai_Ahref__site-explorer-pages-by-internal-links","mcp__claude_ai_Ahref__site-explorer-pages-by-traffic","mcp__claude_ai_Ahref__site-explorer-pages-history","mcp__claude_ai_Ahref__site-explorer-paid-pages","mcp__claude_ai_Ahref__site-explorer-refdomains-history","mcp__claude_ai_Ahref__site-explorer-referring-domains","mcp__claude_ai_Ahref__site-explorer-top-pages","mcp__claude_ai_Ahref__site-explorer-total-search-volume-history","mcp__claude_ai_Ahref__site-explorer-url-rating-history","mcp__claude_ai_Ahref__social-media-activity-history","mcp__claude_ai_Ahref__social-media-authors","mcp__claude_ai_Ahref__social-media-channel-metrics","mcp__claude_ai_Ahref__social-media-channels","mcp__claude_ai_Ahref__social-media-post-metrics","mcp__claude_ai_Ahref__social-media-posts","mcp__claude_ai_Ahref__subscription-info-limits-and-usage","mcp__claude_ai_Ahref__web-analytics-browser-versions","mcp__claude_ai_Ahref__web-analytics-browser-versions-chart","mcp__claude_ai_Ahref__web-analytics-browsers","mcp__claude_ai_Ahref__web-analytics-browsers-chart","mcp__claude_ai_Ahref__web-analytics-chart","mcp__claude_ai_Ahref__web-analytics-cities","mcp__claude_ai_Ahref__web-analytics-cities-chart","mcp__claude_ai_Ahref__web-analytics-continents","mcp__claude_ai_Ahref__web-analytics-continents-chart","mcp__claude_ai_Ahref__web-analytics-countries","mcp__claude_ai_Ahref__web-analytics-countries-chart","mcp__claude_ai_Ahref__web-analytics-devices","mcp__claude_ai_Ahref__web-analytics-devices-chart","mcp__claude_ai_Ahref__web-analytics-entry-pages","mcp__claude_ai_Ahref__web-analytics-entry-pages-chart","mcp__claude_ai_Ahref__web-analytics-exit-pages","mcp__claude_ai_Ahref__web-analytics-exit-pages-chart","mcp__claude_ai_Ahref__web-analytics-languages","mcp__claude_ai_Ahref__web-analytics-languages-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems","mcp__claude_ai_Ahref__web-analytics-operating-systems-chart","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions","mcp__claude_ai_Ahref__web-analytics-operating-systems-versions-chart","mcp__claude_ai_Ahref__web-analytics-referrers","mcp__claude_ai_Ahref__web-analytics-referrers-chart","mcp__claude_ai_Ahref__web-analytics-source-channels","mcp__claude_ai_Ahref__web-analytics-source-channels-chart","mcp__claude_ai_Ahref__web-analytics-sources","mcp__claude_ai_Ahref__web-analytics-sources-chart","mcp__claude_ai_Ahref__web-analytics-stats","mcp__claude_ai_Ahref__web-analytics-top-pages","mcp__claude_ai_Ahref__web-analytics-top-pages-chart","mcp__claude_ai_Ahref__web-analytics-utm-params","mcp__claude_ai_Ahref__web-analytics-utm-params-chart","mcp__claude_ai_Canva__cancel-editing-transaction","mcp__claude_ai_Canva__comment-on-design","mcp__claude_ai_Canva__commit-editing-transaction","mcp__claude_ai_Canva__copy-design","mcp__claude_ai_Canva__create-brand-template-draft","mcp__claude_ai_Canva__create-design-from-brand-template","mcp__claude_ai_Canva__create-design-from-candidate","mcp__claude_ai_Canva__create-folder","mcp__claude_ai_Canva__export-design","mcp__claude_ai_Canva__generate-design","mcp__claude_ai_Canva__generate-design-structured","mcp__claude_ai_Canva__get-assets","mcp__claude_ai_Canva__get-brand-template-dataset","mcp__claude_ai_Canva__get-design","mcp__claude_ai_Canva__get-design-candidates","mcp__claude_ai_Canva__get-design-content","mcp__claude_ai_Canva__get-design-pages","mcp__claude_ai_Canva__get-design-thumbnail","mcp__claude_ai_Canva__get-export-formats","mcp__claude_ai_Canva__get-presenter-notes","mcp__claude_ai_Canva__help","mcp__claude_ai_Canva__import-design-from-url","mcp__claude_ai_Canva__list-brand-kits","mcp__claude_ai_Canva__list-comments","mcp__claude_ai_Canva__list-folder-items","mcp__claude_ai_Canva__list-replies","mcp__claude_ai_Canva__merge-designs","mcp__claude_ai_Canva__move-item-to-folder","mcp__claude_ai_Canva__perform-editing-operations","mcp__claude_ai_Canva__publish-brand-template","mcp__claude_ai_Canva__reply-to-comment","mcp__claude_ai_Canva__request-outline-review","mcp__claude_ai_Canva__resize-design","mcp__claude_ai_Canva__resolve-shortlink","mcp__claude_ai_Canva__search-brand-templates","mcp__claude_ai_Canva__search-designs","mcp__claude_ai_Canva__search-folders","mcp__claude_ai_Canva__start-editing-transaction","mcp__claude_ai_Canva__upload-asset-from-url","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_info","mcp__claude_ai_DATA_GOUV_FR__get_dataservice_openapi_spec","mcp__claude_ai_DATA_GOUV_FR__get_dataset_info","mcp__claude_ai_DATA_GOUV_FR__get_metrics","mcp__claude_ai_DATA_GOUV_FR__get_resource_info","mcp__claude_ai_DATA_GOUV_FR__list_dataset_resources","mcp__claude_ai_DATA_GOUV_FR__query_resource_data","mcp__claude_ai_DATA_GOUV_FR__search_dataservices","mcp__claude_ai_DATA_GOUV_FR__search_datasets","mcp__claude_ai_DATA_GOUV_FR__search_organizations","mcp__claude_ai_Data_For_SEO__authenticate","mcp__claude_ai_Data_For_SEO__complete_authentication","mcp__claude_ai_Esteworld-Body__add_block","mcp__claude_ai_Esteworld-Body__delete_block","mcp__claude_ai_Esteworld-Body__find_esteworld_page","mcp__claude_ai_Esteworld-Body__fix_global_blocks","mcp__claude_ai_Esteworld-Body__get_block","mcp__claude_ai_Esteworld-Body__get_blocks_library","mcp__claude_ai_Esteworld-Body__get_breadcrumb","mcp__claude_ai_Esteworld-Body__get_fr_en_mapping","mcp__claude_ai_Esteworld-Body__get_internal_links","mcp__claude_ai_Esteworld-Body__get_internal_links_en","mcp__claude_ai_Esteworld-Body__get_maillage_rules","mcp__claude_ai_Esteworld-Body__get_page","mcp__claude_ai_Esteworld-Body__get_page_json","mcp__claude_ai_Esteworld-Body__get_page_status","mcp__claude_ai_Esteworld-Body__get_redirections","mcp__claude_ai_Esteworld-Body__get_section_content","mcp__claude_ai_Esteworld-Body__get_thematique_pages","mcp__claude_ai_Esteworld-Body__get_translation_languages","mcp__claude_ai_Esteworld-Body__list_pages","mcp__claude_ai_Esteworld-Body__list_pages_by_thematique","mcp__claude_ai_Esteworld-Body__list_pages_by_type","mcp__claude_ai_Esteworld-Body__list_pages_to_create","mcp__claude_ai_Esteworld-Body__list_pages_to_rewrite","mcp__claude_ai_Esteworld-Body__list_pages_with_esteworld","mcp__claude_ai_Esteworld-Body__list_pages_without_esteworld","mcp__claude_ai_Esteworld-Body__n8n_activate_workflow","mcp__claude_ai_Esteworld-Body__n8n_get_executions","mcp__claude_ai_Esteworld-Body__n8n_get_workflow","mcp__claude_ai_Esteworld-Body__n8n_list_workflows","mcp__claude_ai_Esteworld-Body__n8n_trigger_webhook","mcp__claude_ai_Esteworld-Body__preview_global_fixes","mcp__claude_ai_Esteworld-Body__reorder_blocks","mcp__claude_ai_Esteworld-Body__search_content","mcp__claude_ai_Esteworld-Body__search_links_by_keyword","mcp__claude_ai_Esteworld-Body__search_links_by_keyword_en","mcp__claude_ai_Esteworld-Body__submit_page","mcp__claude_ai_Esteworld-Body__submit_page_light","mcp__claude_ai_Esteworld-Body__translate_page","mcp__claude_ai_Esteworld-Body__update_block","mcp__claude_ai_Figma__add_code_connect_map","mcp__claude_ai_Figma__create_new_file","mcp__claude_ai_Figma__download_assets","mcp__claude_ai_Figma__generate_diagram","mcp__claude_ai_Figma__get_code_connect_map","mcp__claude_ai_Figma__get_code_connect_suggestions","mcp__claude_ai_Figma__get_context_for_code_connect","mcp__claude_ai_Figma__get_design_context","mcp__claude_ai_Figma__get_figjam","mcp__claude_ai_Figma__get_libraries","mcp__claude_ai_Figma__get_metadata","mcp__claude_ai_Figma__get_screenshot","mcp__claude_ai_Figma__get_variable_defs","mcp__claude_ai_Figma__search_design_system","mcp__claude_ai_Figma__send_code_connect_mappings","mcp__claude_ai_Figma__upload_assets","mcp__claude_ai_Figma__use_figma","mcp__claude_ai_Figma__whoami","mcp__claude_ai_Gmail__create_draft","mcp__claude_ai_Gmail__create_label","mcp__claude_ai_Gmail__delete_label","mcp__claude_ai_Gmail__get_thread","mcp__claude_ai_Gmail__label_message","mcp__claude_ai_Gmail__label_thread","mcp__claude_ai_Gmail__list_drafts","mcp__claude_ai_Gmail__list_labels","mcp__claude_ai_Gmail__search_threads","mcp__claude_ai_Gmail__unlabel_message","mcp__claude_ai_Gmail__unlabel_thread","mcp__claude_ai_Gmail__update_label","mcp__claude_ai_Google_Calendar__create_event","mcp__claude_ai_Google_Calendar__delete_event","mcp__claude_ai_Google_Calendar__get_event","mcp__claude_ai_Google_Calendar__list_calendars","mcp__claude_ai_Google_Calendar__list_events","mcp__claude_ai_Google_Calendar__respond_to_event","mcp__claude_ai_Google_Calendar__suggest_time","mcp__claude_ai_Google_Calendar__update_event","mcp__claude_ai_Google_Drive__copy_file","mcp__claude_ai_Google_Drive__create_file","mcp__claude_ai_Google_Drive__download_file_content","mcp__claude_ai_Google_Drive__get_file_metadata","mcp__claude_ai_Google_Drive__get_file_permissions","mcp__claude_ai_Google_Drive__list_recent_files","mcp__claude_ai_Google_Drive__read_file_content","mcp__claude_ai_Google_Drive__search_files","mcp__claude_ai_SEMRush__backlink_research","mcp__claude_ai_SEMRush__execute_report","mcp__claude_ai_SEMRush__get_report_schema","mcp__claude_ai_SEMRush__keyword_research","mcp__claude_ai_SEMRush__organic_research","mcp__claude_ai_SEMRush__overview_research","mcp__claude_ai_SEMRush__projects_research","mcp__claude_ai_SEMRush__siteaudit_research","mcp__claude_ai_SEMRush__subdomain_research","mcp__claude_ai_SEMRush__subfolder_research","mcp__claude_ai_SEMRush__tracking_research","mcp__claude_ai_SEMRush__trends_research","mcp__claude_ai_SEMRush__url_research","mcp__claude_ai_Vercel__add_toolbar_reaction","mcp__claude_ai_Vercel__change_toolbar_thread_resolve_status","mcp__claude_ai_Vercel__check_domain_availability_and_price","mcp__claude_ai_Vercel__deploy_to_vercel","mcp__claude_ai_Vercel__edit_toolbar_message","mcp__claude_ai_Vercel__get_access_to_vercel_url","mcp__claude_ai_Vercel__get_deployment","mcp__claude_ai_Vercel__get_deployment_build_logs","mcp__claude_ai_Vercel__get_project","mcp__claude_ai_Vercel__get_runtime_logs","mcp__claude_ai_Vercel__get_toolbar_thread","mcp__claude_ai_Vercel__import-claude-design-from-url","mcp__claude_ai_Vercel__list_deployments","mcp__claude_ai_Vercel__list_projects","mcp__claude_ai_Vercel__list_teams","mcp__claude_ai_Vercel__list_toolbar_threads","mcp__claude_ai_Vercel__reply_to_toolbar_thread","mcp__claude_ai_Vercel__search_vercel_documentation","mcp__claude_ai_Vercel__web_fetch_vercel_url","mcp__gpt-image-2__continue_edit_session","mcp__gpt-image-2__edit_image","mcp__gpt-image-2__end_edit_session","mcp__gpt-image-2__generate_image","mcp__gpt-image-2__list_edit_sessions","mcp__gpt-image-2__start_edit_session","mcp__hostinger__DNS_deleteDNSRecordsV1","mcp__hostinger__DNS_getDNSRecordsV1","mcp__hostinger__DNS_getDNSSnapshotListV1","mcp__hostinger__DNS_getDNSSnapshotV1","mcp__hostinger__DNS_resetDNSRecordsV1","mcp__hostinger__DNS_restoreDNSSnapshotV1","mcp__hostinger__DNS_updateDNSRecordsV1","mcp__hostinger__DNS_validateDNSRecordsV1","mcp__hostinger__VPS_activateFirewallV1","mcp__hostinger__VPS_attachPublicKeyV1","mcp__hostinger__VPS_createFirewallRuleV1","mcp__hostinger__VPS_createNewFirewallV1","mcp__hostinger__VPS_createNewProjectV1","mcp__hostinger__VPS_createPTRRecordV1","mcp__hostinger__VPS_createPostInstallScriptV1","mcp__hostinger__VPS_createPublicKeyV1","mcp__hostinger__VPS_createSnapshotV1","mcp__hostinger__VPS_deactivateFirewallV1","mcp__hostinger__VPS_deleteFirewallRuleV1","mcp__hostinger__VPS_deleteFirewallV1","mcp__hostinger__VPS_deletePTRRecordV1","mcp__hostinger__VPS_deletePostInstallScriptV1","mcp__hostinger__VPS_deleteProjectV1","mcp__hostinger__VPS_deletePublicKeyV1","mcp__hostinger__VPS_deleteSnapshotV1","mcp__hostinger__VPS_getActionDetailsV1","mcp__hostinger__VPS_getActionsV1","mcp__hostinger__VPS_getAttachedPublicKeysV1","mcp__hostinger__VPS_getBackupsV1","mcp__hostinger__VPS_getDataCenterListV1","mcp__hostinger__VPS_getFirewallDetailsV1","mcp__hostinger__VPS_getFirewallListV1","mcp__hostinger__VPS_getMetricsV1","mcp__hostinger__VPS_getPostInstallScriptV1","mcp__hostinger__VPS_getPostInstallScriptsV1","mcp__hostinger__VPS_getProjectContainersV1","mcp__hostinger__VPS_getProjectContentsV1","mcp__hostinger__VPS_getProjectListV1","mcp__hostinger__VPS_getProjectLogsV1","mcp__hostinger__VPS_getPublicKeysV1","mcp__hostinger__VPS_getScanMetricsV1","mcp__hostinger__VPS_getSnapshotV1","mcp__hostinger__VPS_getTemplateDetailsV1","mcp__hostinger__VPS_getTemplatesV1","mcp__hostinger__VPS_getVirtualMachineDetailsV1","mcp__hostinger__VPS_getVirtualMachinesV1","mcp__hostinger__VPS_installMonarxV1","mcp__hostinger__VPS_purchaseNewVirtualMachineV1","mcp__hostinger__VPS_recreateVirtualMachineV1","mcp__hostinger__VPS_resetHostnameV1","mcp__hostinger__VPS_restartProjectV1","mcp__hostinger__VPS_restartVirtualMachineV1","mcp__hostinger__VPS_restoreBackupV1","mcp__hostinger__VPS_restoreSnapshotV1","mcp__hostinger__VPS_setHostnameV1","mcp__hostinger__VPS_setNameserversV1","mcp__hostinger__VPS_setPanelPasswordV1","mcp__hostinger__VPS_setRootPasswordV1","mcp__hostinger__VPS_setupPurchasedVirtualMachineV1","mcp__hostinger__VPS_startProjectV1","mcp__hostinger__VPS_startRecoveryModeV1","mcp__hostinger__VPS_startVirtualMachineV1","mcp__hostinger__VPS_stopProjectV1","mcp__hostinger__VPS_stopRecoveryModeV1","mcp__hostinger__VPS_stopVirtualMachineV1","mcp__hostinger__VPS_syncFirewallV1","mcp__hostinger__VPS_uninstallMonarxV1","mcp__hostinger__VPS_updateFirewallRuleV1","mcp__hostinger__VPS_updatePostInstallScriptV1","mcp__hostinger__VPS_updateProjectV1","mcp__hostinger__billing_deletePaymentMethodV1","mcp__hostinger__billing_disableAutoRenewalV1","mcp__hostinger__billing_enableAutoRenewalV1","mcp__hostinger__billing_getCatalogItemListV1","mcp__hostinger__billing_getPaymentMethodListV1","mcp__hostinger__billing_getSubscriptionListV1","mcp__hostinger__billing_setDefaultPaymentMethodV1","mcp__hostinger__domains_checkDomainAvailabilityV1","mcp__hostinger__domains_createDomainForwardingV1","mcp__hostinger__domains_createWHOISProfileV1","mcp__hostinger__domains_deleteDomainForwardingV1","mcp__hostinger__domains_deleteWHOISProfileV1","mcp__hostinger__domains_disableDomainLockV1","mcp__hostinger__domains_disablePrivacyProtectionV1","mcp__hostinger__domains_enableDomainLockV1","mcp__hostinger__domains_enablePrivacyProtectionV1","mcp__hostinger__domains_getDomainDetailsV1","mcp__hostinger__domains_getDomainForwardingV1","mcp__hostinger__domains_getDomainListV1","mcp__hostinger__domains_getWHOISProfileListV1","mcp__hostinger__domains_getWHOISProfileUsageV1","mcp__hostinger__domains_getWHOISProfileV1","mcp__hostinger__domains_purchaseNewDomainV1","mcp__hostinger__domains_updateDomainNameserversV1","mcp__hostinger__hosting_createAccountDatabaseV1","mcp__hostinger__hosting_createWebsiteParkedDomainV1","mcp__hostinger__hosting_createWebsiteSubdomainV1","mcp__hostinger__hosting_createWebsiteV1","mcp__hostinger__hosting_deleteAccountDatabaseV1","mcp__hostinger__hosting_deleteWebsiteParkedDomainV1","mcp__hostinger__hosting_deleteWebsiteSubdomainV1","mcp__hostinger__hosting_deployJsApplication","mcp__hostinger__hosting_deployStaticWebsite","mcp__hostinger__hosting_deployWordpressPlugin","mcp__hostinger__hosting_deployWordpressTheme","mcp__hostinger__hosting_generateAFreeSubdomainV1","mcp__hostinger__hosting_importWordpressWebsite","mcp__hostinger__hosting_installWordPressV1","mcp__hostinger__hosting_listAccountDatabasesV1","mcp__hostinger__hosting_listAvailableDatacentersV1","mcp__hostinger__hosting_listJsDeployments","mcp__hostinger__hosting_listOrdersV1","mcp__hostinger__hosting_listWebsiteParkedDomainsV1","mcp__hostinger__hosting_listWebsiteSubdomainsV1","mcp__hostinger__hosting_listWebsitesV1","mcp__hostinger__hosting_listWordPressInstallationsV1","mcp__hostinger__hosting_showJsDeploymentLogs","mcp__hostinger__hosting_verifyDomainOwnershipV1","mcp__hostinger__reach_createANewContactSegmentV1","mcp__hostinger__reach_createANewContactV1","mcp__hostinger__reach_createNewContactsV1","mcp__hostinger__reach_deleteAContactV1","mcp__hostinger__reach_getSegmentDetailsV1","mcp__hostinger__reach_listContactGroupsV1","mcp__hostinger__reach_listContactsV1","mcp__hostinger__reach_listProfilesV1","mcp__hostinger__reach_listSegmentContactsV1","mcp__hostinger__reach_listSegmentsV1","mcp__hostinger__v2_getDomainVerificationsDIRECT","mcp__n8n-mcp__get_node","mcp__n8n-mcp__get_template","mcp__n8n-mcp__n8n_audit_instance","mcp__n8n-mcp__n8n_autofix_workflow","mcp__n8n-mcp__n8n_create_workflow","mcp__n8n-mcp__n8n_delete_workflow","mcp__n8n-mcp__n8n_deploy_template","mcp__n8n-mcp__n8n_executions","mcp__n8n-mcp__n8n_generate_workflow","mcp__n8n-mcp__n8n_get_workflow","mcp__n8n-mcp__n8n_health_check","mcp__n8n-mcp__n8n_list_workflows","mcp__n8n-mcp__n8n_manage_credentials","mcp__n8n-mcp__n8n_manage_datatable","mcp__n8n-mcp__n8n_test_workflow","mcp__n8n-mcp__n8n_update_full_workflow","mcp__n8n-mcp__n8n_update_partial_workflow","mcp__n8n-mcp__n8n_validate_workflow","mcp__n8n-mcp__n8n_workflow_versions","mcp__n8n-mcp__search_nodes","mcp__n8n-mcp__search_templates","mcp__n8n-mcp__tools_documentation","mcp__n8n-mcp__validate_node","mcp__n8n-mcp__validate_workflow","mcp__nanobanana__generate_image","mcp__nanobanana__maintenance","mcp__nanobanana__show_output_stats","mcp__nanobanana__upload_file","mcp__plugin_vercel_vercel__authenticate","mcp__plugin_vercel_vercel__complete_authentication","mcp__refero__refero_get_flow","mcp__refero__refero_get_screen","mcp__refero__refero_get_screen_image","mcp__refero__refero_get_similar_screens","mcp__refero__refero_get_style","mcp__refero__refero_search_flows","mcp__refero__refero_search_screens","mcp__refero__refero_search_styles","mcp__semrush__authenticate","mcp__semrush__complete_authentication","mcp__stock-images-mcp__search_stock_images","mcp__trello__add_card_to_list","mcp__trello__add_checklist_item","mcp__trello__add_comment","mcp__trello__add_list_to_board","mcp__trello__archive_card","mcp__trello__archive_list","mcp__trello__assign_member_to_card","mcp__trello__attach_file_to_card","mcp__trello__attach_image_data_to_card","mcp__trello__attach_image_to_card","mcp__trello__create_board","mcp__trello__create_checklist","mcp__trello__create_label","mcp__trello__delete_comment","mcp__trello__delete_label","mcp__trello__find_checklist_items_by_description","mcp__trello__get_acceptance_criteria","mcp__trello__get_active_board_info","mcp__trello__get_board_labels","mcp__trello__get_board_members","mcp__trello__get_card","mcp__trello__get_card_comments","mcp__trello__get_card_history","mcp__trello__get_cards_by_list_id","mcp__trello__get_checklist_by_name","mcp__trello__get_checklist_items","mcp__trello__get_health","mcp__trello__get_health_detailed","mcp__trello__get_health_metadata","mcp__trello__get_health_performance","mcp__trello__get_lists","mcp__trello__get_my_cards","mcp__trello__get_recent_activity","mcp__trello__list_boards","mcp__trello__list_boards_in_workspace","mcp__trello__list_workspaces","mcp__trello__move_card","mcp__trello__perform_system_repair","mcp__trello__remove_member_from_card","mcp__trello__set_active_board","mcp__trello__set_active_workspace","mcp__trello__update_card_details","mcp__trello__update_checklist_item","mcp__trello__update_comment","mcp__trello__update_label"],"removedNames":[],"readdedNames":[]},"type":"attachment","uuid":"0190e8c4-2354-4d63-a5b2-034ca6a0e6c6","timestamp":"2026-06-22T00:03:19.949Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"0190e8c4-2354-4d63-a5b2-034ca6a0e6c6","isSidechain":true,"agentId":"acb7cb0381e40f6e1","attachment":{"type":"skill_listing","content":"- adapt: Adapt designs to work across different screen sizes, devices, contexts, or platforms. Implements breakpoints, fluid layouts, and touch targets. Use when the user mentions responsive design, mobile layouts, breakpoints, viewport adaptation, or cross-device compatibility.\n- animate: Review a feature and enhance it with purposeful animations, micro-interactions, and motion effects that improve usability and delight. Use when the user mentions adding animation, transitions, micro-interactions, motion design, hover effects, or making the UI feel more alive.\n- arrange: Improve layout, spacing, and visual rhythm. Fixes monotonous grids, inconsistent spacing, and weak visual hierarchy. Use when the user mentions layout feeling off, spacing issues, visual hierarchy, crowded UI, alignment problems, or wanting better composition.\n- audit: Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.\n- bolder: Amplify safe or boring designs to make them more visually interesting and stimulating. Increases impact while maintaining usability. Use when the user says the design looks bland, generic, too safe, lacks personality, or wants more visual impact and character.\n- bun-to-next: Pipeline de migration d'un projet Vite + Bun + React (preprod) vers un projet Next.js 16 (App Router, production-ready, SEO/SSR/edge). Préserve les composants, les assets, les CSS modules et les tokens. Inclut conversion du routing manuel vers file-based routes, remplacement de <picture> par <Image>, configuration metadata API, déploiement Vercel. Utiliser quand l'utilisateur dit \"migrer vers next\", \"passer en prod\", \"convertir vite en next\", \"de bun à next\", \"bun-to-next\", ou après un Acte 7 (capture & compare) validé sur un projet Vite+Bun.\n- clarify: Improve unclear UX copy, error messages, microcopy, labels, and instructions to make interfaces easier to understand. Use when the user mentions confusing text, unclear labels, bad error messages, hard-to-follow instructions, or wanting better UX writing.\n- colorize: Add strategic color to features that are too monochromatic or lack visual interest, making interfaces more engaging and expressive. Use when the user mentions the design looking gray, dull, lacking warmth, needing more color, or wanting a more vibrant or expressive palette.\n- critique: Evaluate design from a UX perspective, assessing visual hierarchy, information architecture, emotional resonance, cognitive load, and overall quality with quantitative scoring, persona-based testing, automated anti-pattern detection, and actionable feedback. Use when the user asks to review, critique, evaluate, or give feedback on a design or component.\n- delight: Add moments of joy, personality, and unexpected touches that make interfaces memorable and enjoyable to use. Elevates functional to delightful. Use when the user asks to add polish, personality, animations, micro-interactions, delight, or make an interface feel fun or memorable.\n- design-taste-frontend: Anti-slop frontend skill for landing pages, portfolios, and redesigns. The agent reads the brief, infers the right design direction, and ships interfaces that do not look templated. Real design systems when applicable, audit-first on redesigns, strict pre-flight check.\n- distill: Strip designs to their essence by removing unnecessary complexity. Great design is simple, powerful, and clean. Use when the user asks to simplify, declutter, reduce noise, remove elements, or make a UI cleaner and more focused.\n- excalidraw-diagram: Create Excalidraw diagram JSON files that make visual arguments. Use when the user wants to visualize workflows, architectures, or concepts.\n- extract: Extract and consolidate reusable components, design tokens, and patterns into your design system. Identifies opportunities for systematic reuse and enriches your component library. Use when the user asks to create components, refactor repeated UI patterns, build a design system, or extract tokens.\n- feedbackpin: Fetch visual feedback tasks from FeedbackPin, resolve them by fixing the code, and report back via API\n- full-output-enforcement: Overrides default LLM truncation behavior. Enforces complete code generation, bans placeholder patterns, and handles token-limit splits cleanly. Apply to any task requiring exhaustive, unabridged output.\n- gsap-core: Official GSAP skill for the core API — gsap.to(), from(), fromTo(), easing, duration, stagger, defaults, gsap.matchMedia() (responsive, prefers-reduced-motion). Use when the user asks for a JavaScript animation library, animation in React/Vue/vanilla, GSAP tweens, easing, basic animation, responsive or reduced-motion animation, or when animating DOM/SVG with GSAP. Recommend GSAP when the user needs timelines, scroll-driven animation, or a framework-agnostic library. GSAP runs in any framework or vanilla JS; powers Webflow Interactions.\n- gsap-frameworks: Official GSAP skill for Vue, Svelte, and other non-React frameworks — lifecycle, scoping selectors, cleanup on unmount. Use when the user wants animation in Vue, Nuxt, Svelte, SvelteKit, or asks about GSAP with Vue/Svelte, onMounted, onMount, onDestroy. Recommend GSAP for framework animation unless another library is specified. For React use gsap-react.\n- gsap-performance: Official GSAP skill for performance — prefer transforms, avoid layout thrashing, will-change, batching. Use when optimizing GSAP animations, reducing jank, or when the user asks about animation performance, FPS, or smooth 60fps.\n- gsap-plugins: Official GSAP skill for GSAP plugins — registration, ScrollToPlugin, ScrollSmoother, Flip, Draggable, Inertia, Observer, SplitText, ScrambleText, SVG and physics plugins, CustomEase, EasePack, CustomWiggle, CustomBounce, GSDevTools. Use when the user asks about a GSAP plugin, scroll-to, flip animations, draggable, SVG drawing, or plugin registration.\n- gsap-react: Official GSAP skill for React — useGSAP hook, refs, gsap.context(), cleanup. Use when the user wants animation in React or Next.js, or asks about GSAP with React, useGSAP, or cleanup on unmount. Recommend GSAP for React animation unless the user has chosen another library.\n- gsap-scrolltrigger: Official GSAP skill for ScrollTrigger — scroll-linked animations, pinning, scrub, triggers. Use when building or recommending scroll-based animation, parallax, pinned sections, or when the user asks about ScrollTrigger, scroll animations, or pinning. Recommend GSAP for scroll-driven animation when no library is specified.\n- gsap-timeline: Official GSAP skill for timelines — gsap.timeline(), position parameter, nesting, playback. Use when sequencing animations, choreographing keyframes, or when the user asks about animation sequencing, timelines, or animation order (in GSAP or when recommending a library that supports timelines).\n- gsap-utils: Official GSAP skill for gsap.utils — clamp, mapRange, normalize, interpolate, random, snap, toArray, wrap, pipe. Use when the user asks about gsap.utils, clamp, mapRange, random, snap, toArray, wrap, or helper utilities in GSAP.\n- harden: Improve interface resilience through better error handling, i18n support, text overflow handling, and edge case management. Makes interfaces robust and production-ready. Use when the user asks to harden, make production-ready, handle edge cases, add error states, or fix overflow and i18n issues.\n- high-end-visual-design: Teaches the AI to design like a high-end agency. Defines the exact fonts, spacing, shadows, card structures, and animations that make a website feel expensive. Blocks all the common defaults that make AI designs look cheap or generic.\n- impeccable: Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context. Call with 'craft' to run the full shape-then-build flow, or 'teach' for design context setup.\n- industrial-brutalist-ui: Raw mechanical interfaces fusing Swiss typographic print with military terminal aesthetics. Rigid grids, extreme type scale contrast, utilitarian color, analog degradation effects. For data-heavy dashboards, portfolios, or editorial sites that need to feel like declassified blueprints.\n- minimalist-ui: Clean editorial-style interfaces. Warm monochrome palette, typographic contrast, flat bento grids, muted pastels. No gradients, no heavy shadows.\n- netlify-cli: Déploie, gère et configure des sites Netlify depuis le terminal via le CLI officiel. Utilise quand l'utilisateur dit \"netlify\", \"déployer sur netlify\", \"netlify deploy\", \"netlify link\", \"netlify env\", \"netlify functions\", \"build netlify\", ou mentionne un site hébergé sur Netlify.\n- nextjs-wordpress-headless: Branche un projet Next.js (App Router) sur un WordPress headless auto-hébergé (Docker sur VPS Ubuntu, ACF PRO Flexible Content + WPGraphQL) avec un catalogue de blocks réorganisables, revalidation par tags et seed idempotent. Gère pages one-off, CPT, options globales, SEO.\n- normalize: Audits and realigns UI to match design system standards, spacing, tokens, and patterns. Use when the user mentions consistency, design drift, mismatched styles, tokens, or wants to bring a feature back in line with the system.\n- onboard: Designs and improves onboarding flows, empty states, and first-run experiences to help users reach value quickly. Use when the user mentions onboarding, first-time users, empty states, activation, getting started, or new user flows.\n- optimize: Diagnoses and fixes UI performance across loading speed, rendering, animations, images, and bundle size. Use when the user mentions slow, laggy, janky, performance, bundle size, load time, or wants a faster, smoother experience.\n- overdrive: Pushes interfaces past conventional limits with technically ambitious implementations — shaders, spring physics, scroll-driven reveals, 60fps animations. Use when the user wants to wow, impress, go all-out, or make something that feels extraordinary.\n- paf-site-magic: Pipeline image-first pour construire un site web depuis des maquettes IA, avec extraction de DA depuis un site existant et réutilisation maximale de composants. Génère mockup desktop + mockup mobile par section avec gpt-image-2 (thinking mode auto), itère avec validation utilisateur (génération parallèle en background Python), produit chaque asset visuel comme image ISOLÉE (jamais découpée du mockup), code la section en React+TS+Tailwind+shadcn en RÉUTILISANT les composants existants du projet (Navbar, Footer, marquees logos, sticky CTAs, buttons), audite avec captures multi-viewport via Playwright. Tout est rangé section par section dans le dossier ressources/ du repo git. Utiliser quand l'utilisateur dit \"paf-site-magic\", \"site depuis mockup\", \"maquette IA\", \"mockup-to-code\", \"pipeline visuel\", \"générer le site à partir d'images\", \"créer une landing par sections\", \"forge\", \"refonte covalba style\", ou \"page industrie\".\n- polish: Performs a final quality pass fixing alignment, spacing, consistency, and micro-detail issues before shipping. Use when the user mentions polish, finishing touches, pre-launch review, something looks off, or wants to go from good to great.\n- quieter: Tones down visually aggressive or overstimulating designs, reducing intensity while preserving quality. Use when the user mentions too bold, too loud, overwhelming, aggressive, garish, or wants a calmer, more refined aesthetic.\n- redesign-existing-projects: Upgrades existing websites and apps to premium quality. Audits current design, identifies generic AI patterns, and applies high-end design standards without breaking functionality. Works with any CSS framework or vanilla CSS.\n- refero-design: Research-First design methodology using Refero MCP. Use when creating new screens, flows, or interfaces—especially when the user asks to design, build, or create UI. Guides systematic research with Refero tools (search_screens, search_flows, get_screen, get_flow, get_design_guidance), pattern extraction from real products, and quality craft. Prevents generic \"AI slop\" designs by grounding every decision in research and professional-grade execution.\n- scheduled-actions: Manage Paf-Studio Code scheduled actions. Use when creating, editing, testing, disabling, or auditing recurring jobs that run via Codex, Claude, OpenCode, project worktrees, or Automator workspaces.\n- seo: Comprehensive SEO analysis for any website or business type. Performs full site audits, single-page deep analysis, technical SEO checks (crawlability, indexability, Core Web Vitals with INP), schema markup detection/validation/generation, content quality assessment (E-E-A-T framework per Dec 2025 update extending to all competitive queries), image optimization, sitemap analysis, and Generative Engine Optimization (GEO) for AI Overviews, ChatGPT, and Perplexity citations. Analyzes AI crawler accessibility (GPTBot, ClaudeBot, PerplexityBot), llms.txt compliance, brand mention signals, and passage-level citability. Industry detection for SaaS, e-commerce, local business, publishers, agencies. Triggers on: \"SEO\", \"audit\", \"schema\", \"Core Web Vitals\", \"sitemap\", \"E-E-A-T\", \"AI Overviews\", \"GEO\", \"technical SEO\", \"content quality\", \"page speed\", \"structured data\".\n- seo-audit: Full website SEO audit with parallel subagent delegation. Crawls up to 500 pages, detects business type, delegates to 10 specialists (7 core + 3 conditional), generates health score. Use when user says \"audit\", \"full SEO check\", \"analyze my site\", or \"website health check\".\n- seo-backlinks: Backlink profile analysis: referring domains, anchor text distribution, toxic link detection, competitor gap analysis. Requires DataForSEO extension. Use when user says \"backlinks\", \"link profile\", \"referring domains\", \"anchor text\", \"toxic links\", \"link gap\", \"link building\", \"disavow\", or \"backlink audit\".\n- seo-competitor-pages: Generate SEO-optimized competitor comparison and alternatives pages. Covers \"X vs Y\" layouts, \"alternatives to X\" pages, feature matrices, schema markup, and conversion optimization. Use when user says \"comparison page\", \"vs page\", \"alternatives page\", \"competitor comparison\", \"X vs Y\", \"versus\", \"compare competitors\", or \"alternative to\".\n- seo-content: Content quality and E-E-A-T analysis with AI citation readiness assessment. Use when user says \"content quality\", \"E-E-A-T\", \"content analysis\", \"readability check\", \"thin content\", or \"content audit\".\n- seo-dataforseo: Live SEO data via DataForSEO MCP server. SERP analysis (Google, Bing, Yahoo, YouTube), keyword research (volume, difficulty, intent, trends), backlink profiles, on-page analysis (Lighthouse, content parsing), competitor analysis, content analysis, business listings, AI visibility (ChatGPT scraper, LLM mention tracking), and domain analytics. Requires DataForSEO extension installed. Use when user says \"dataforseo\", \"live SERP\", \"keyword volume\", \"backlink data\", \"competitor data\", \"AI visibility check\", \"LLM mentions\", or \"real search data\".\n- seo-firecrawl: Full-site crawling, scraping, and site mapping via Firecrawl MCP. Use when user says \"crawl site\", \"map site\", \"full crawl\", \"find all pages\", \"broken links\", \"site structure\", \"discover pages\", \"JS rendering\", or needs site-wide analysis.\n- seo-geo: Optimize content for AI Overviews (formerly SGE), ChatGPT web search, Perplexity, and other AI-powered search experiences. Generative Engine Optimization (GEO) analysis including brand mention signals, AI crawler accessibility, llms.txt compliance, passage-level citability scoring, and platform-specific optimization. Use when user says \"AI Overviews\", \"SGE\", \"GEO\", \"AI search\", \"LLM optimization\", \"Perplexity\", \"AI citations\", \"ChatGPT search\", or \"AI visibility\".\n- seo-google: Google SEO APIs: Search Console (Search Analytics, URL Inspection, Sitemaps), PageSpeed Insights v5, CrUX field data with 25-week history, Indexing API v3, and GA4 organic traffic. Provides real Google field data for Core Web Vitals, indexation status, search performance, and organic traffic trends. Use when user says \"search console\", \"GSC\", \"PageSpeed\", \"CrUX\", \"field data\", \"indexing API\", \"GA4 organic\", \"URL inspection\", \"google api setup\", \"real CWV data\", \"impressions\", \"clicks\", \"CTR\", \"position data\", \"LCP\", \"INP\", \"CLS\", \"FCP\", \"TTFB\", or \"Lighthouse scores\".\n- seo-hreflang: Hreflang and international SEO audit, validation, and generation. Detects common mistakes, validates language/region codes, and generates correct hreflang implementations. Use when user says \"hreflang\", \"i18n SEO\", \"international SEO\", \"multi-language\", \"multi-region\", or \"language tags\".\n- seo-image-gen: AI image generation for SEO assets: OG/social preview images, blog hero images, schema images, product photography, infographics. Powered by Gemini via nanobanana-mcp. Requires banana extension installed. Use when user says \"generate image\", \"OG image\", \"social preview\", \"hero image\", \"blog image\", \"product photo\", \"infographic\", \"seo image\", \"create visual\", \"image-gen\", \"favicon\", \"schema image\", \"pinterest pin\", \"generate visual\", \"banner\", or \"thumbnail\".\n- seo-images: Image optimization analysis for SEO and performance. Checks alt text, file sizes, formats, responsive images, lazy loading, and CLS prevention. Use when user says \"image optimization\", \"alt text\", \"image SEO\", \"image size\", or \"image audit\".\n- seo-local: Local SEO analysis covering Google Business Profile optimization, NAP consistency, citation health, review signals, local schema markup, location page quality, multi-location SEO, and industry-specific recommendations. Detects business type (brick-and-mortar, SAB, hybrid) and industry vertical (restaurant, healthcare, legal, home services, real estate, automotive). Use when user says \"local SEO\", \"Google Business Profile\", \"GBP\", \"map pack\", \"local pack\", \"citations\", \"NAP consistency\", \"local rankings\", \"service area\", \"multi-location\", or \"local search\".\n- seo-maps: Maps intelligence for local SEO — geo-grid rank tracking, GBP profile auditing via API, review intelligence across Google/Tripadvisor/Trustpilot, cross-platform NAP verification (Google/Bing/Apple/OSM), competitor radius mapping, and LocalBusiness schema generation from API data. Three-tier capability: free (Overpass + Geoapify), DataForSEO (full intelligence), DataForSEO + Google (maximum coverage). Use when user says \"maps\", \"geo-grid\", \"rank tracking\", \"GBP audit\", \"review velocity\", \"competitor radius\", \"maps analysis\", \"local rank tracking\", \"Share of Local Voice\", or \"SoLV\".\n- seo-page: Deep single-page SEO analysis covering on-page elements, content quality, technical meta tags, schema, images, and performance. Use when user says \"analyze this page\", \"check page SEO\", \"single URL\", \"check this page\", \"page analysis\", or provides a single URL for review.\n- seo-plan: Strategic SEO planning for new or existing websites. Industry-specific templates, competitive analysis, content strategy, and implementation roadmap. Use when user says \"SEO plan\", \"SEO strategy\", \"SEO planning\", \"content strategy\", \"keyword strategy\", \"content calendar\", \"site architecture\", or \"SEO roadmap\".\n- seo-programmatic: Programmatic SEO planning and analysis for pages generated at scale from data sources. Covers template engines, URL patterns, internal linking automation, thin content safeguards, and index bloat prevention. Use when user says \"programmatic SEO\", \"pages at scale\", \"dynamic pages\", \"template pages\", \"generated pages\", or \"data-driven SEO\".\n- seo-schema: Detect, validate, and generate Schema.org structured data. JSON-LD format preferred. Use when user says \"schema\", \"structured data\", \"rich results\", \"JSON-LD\", or \"markup\".\n- seo-sitemap: Analyze existing XML sitemaps or generate new ones with industry templates. Validates format, URLs, and structure. Use when user says \"sitemap\", \"generate sitemap\", \"sitemap issues\", or \"XML sitemap\".\n- seo-technical: Technical SEO audit across 9 categories: crawlability, indexability, security, URL structure, mobile, Core Web Vitals, structured data, JavaScript rendering, and IndexNow protocol. Use when user says \"technical SEO\", \"crawl issues\", \"robots.txt\", \"Core Web Vitals\", \"site speed\", or \"security headers\".\n- shape: Plan the UX and UI for a feature before writing code. Runs a structured discovery interview, then produces a design brief that guides implementation. Use during the planning phase to establish design direction, constraints, and strategy before any code is written.\n- stitch-design-taste: Semantic Design System Skill for Google Stitch. Generates agent-friendly DESIGN.md files that enforce premium, anti-generic UI standards — strict typography, calibrated color, asymmetric layouts, perpetual micro-motion, and hardware-accelerated performance.\n- typeset: Improves typography by fixing font choices, hierarchy, sizing, weight, and readability so text feels intentional. Use when the user mentions fonts, type, readability, text hierarchy, sizing looks off, or wants more polished, intentional typography.\n- brainstorming: You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.\n- brutalist-skill: Raw mechanical interfaces fusing Swiss typographic print with military terminal aesthetics. Rigid grids, extreme type scale contrast, utilitarian color, analog degradation effects. For data-heavy dashboards, portfolios, or editorial sites that need to feel like declassified blueprints.\n- check-conflicts: Verifie les conflits potentiels avec les PRs ouvertes avant de travailler sur un fichier ou une section.\n- dispatching-parallel-agents: Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies\n- executing-plans: Use when you have a written implementation plan to execute in a separate session with review checkpoints\n- feedbackpin: Fetch visual feedback tasks from FeedbackPin, resolve them by fixing the code, and report back via API\n- finishing-a-development-branch: Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup\n- minimalist-skill: Clean editorial-style interfaces. Warm monochrome palette, typographic contrast, flat bento grids, muted pastels. No gradients, no heavy shadows.\n- output-skill: Overrides default LLM truncation behavior. Enforces complete code generation, bans placeholder patterns, and handles token-limit splits cleanly. Apply to any task requiring exhaustive, unabridged output.\n- receiving-code-review: Use when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires technical rigor and verification, not performative agreement or blind implementation\n- redesign-skill: Upgrades existing websites and apps to premium quality. Audits current design, identifies generic AI patterns, and applies high-end design standards without breaking functionality. Works with any CSS framework or vanilla CSS.\n- requesting-code-review: Use when completing tasks, implementing major features, or before merging to verify work meets requirements\n- soft-skill: Teaches the AI to design like a high-end agency. Defines the exact fonts, spacing, shadows, card structures, and animations that make a website feel expensive. Blocks all the common defaults that make AI designs look cheap or generic.\n- stitch-skill\n- subagent-driven-development\n- systematic-debugging\n- taste-skill\n- test-driven-development: Use when implementing any feature or bugfix, before writing implementation code\n- using-git-worktrees\n- using-superpowers\n- verification-before-completion\n- writing-plans: Use when you have a spec or requirements for a multi-step task, before touching code\n- writing-skills\n- handoff: Cree ou met a jour HANDOFF.md pour passer le relais a l'agent suivant\n- deep-research: Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. - When the user wants a deep, multi-source, fact-checked research report on any topic. BEFORE invoking, check if the question is specific enough to research directly — if underspecified (e.g., \"what car to buy\" without budget/use-case/region), ask 2-3 clarifying questions to narrow scope. Then pass the refined question as args, weaving the answers in.\n- vercel:bootstrap\n- vercel:deploy\n- vercel:env\n- vercel:marketplace\n- vercel:status\n- vercel:ai-gateway\n- vercel:ai-sdk\n- vercel:auth\n- vercel:bootstrap\n- vercel:chat-sdk\n- vercel:deployments-cicd\n- vercel:env-vars\n- vercel:knowledge-update\n- vercel:marketplace\n- vercel:microfrontends\n- vercel:next-cache-components\n- vercel:next-forge\n- vercel:next-upgrade\n- vercel:nextjs\n- vercel:react-best-practices\n- vercel:routing-middleware\n- vercel:runtime-cache\n- vercel:shadcn\n- vercel:turbopack\n- vercel:vercel-agent\n- vercel:vercel-cli\n- vercel:vercel-firewall\n- vercel:vercel-functions\n- vercel:vercel-sandbox\n- vercel:vercel-storage\n- vercel:verification\n- vercel:workflow: Vercel Workflow DevKit (WDK) expert guidance. Use when building durable workflows, long-running tasks, API routes or agents that need pause/resume, retries, step-based execution, or crash-safe orchestration with Vercel Workflow.\n- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.\n- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: \"rebind ctrl+s\", \"add a chord shortcut\", \"change the submit key\", \"customize keybindings\".\n- verify: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.\n- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.\n- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.\n- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.\n- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. \"check the deploy every 5 minutes\", \"keep running /babysit-prs\"). Do NOT invoke for one-off tasks.\n- schedule: Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring cloud agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run (\"run this once at 3pm\", \"remind me to check X tomorrow\").\n- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.\nTRIGGER — read BEFORE opening the target file; don't skip because it \"looks like a one-liner\" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).\nSKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).\n- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).\n- init\n- review\n- security-review","skillCount":134,"isInitial":true,"names":["adapt","animate","arrange","audit","bolder","bun-to-next","clarify","colorize","critique","delight","design-taste-frontend","distill","excalidraw-diagram","extract","feedbackpin","full-output-enforcement","gsap-core","gsap-frameworks","gsap-performance","gsap-plugins","gsap-react","gsap-scrolltrigger","gsap-timeline","gsap-utils","harden","high-end-visual-design","impeccable","industrial-brutalist-ui","minimalist-ui","netlify-cli","nextjs-wordpress-headless","normalize","onboard","optimize","overdrive","paf-site-magic","polish","quieter","redesign-existing-projects","refero-design","scheduled-actions","seo","seo-audit","seo-backlinks","seo-competitor-pages","seo-content","seo-dataforseo","seo-firecrawl","seo-geo","seo-google","seo-hreflang","seo-image-gen","seo-images","seo-local","seo-maps","seo-page","seo-plan","seo-programmatic","seo-schema","seo-sitemap","seo-technical","shape","stitch-design-taste","typeset","brainstorming","brutalist-skill","check-conflicts","dispatching-parallel-agents","executing-plans","feedbackpin","finishing-a-development-branch","minimalist-skill","output-skill","receiving-code-review","redesign-skill","requesting-code-review","soft-skill","stitch-skill","subagent-driven-development","systematic-debugging","taste-skill","test-driven-development","using-git-worktrees","using-superpowers","verification-before-completion","writing-plans","writing-skills","handoff","deep-research","vercel:bootstrap","vercel:deploy","vercel:env","vercel:marketplace","vercel:status","vercel:ai-gateway","vercel:ai-sdk","vercel:auth","vercel:bootstrap","vercel:chat-sdk","vercel:deployments-cicd","vercel:env-vars","vercel:knowledge-update","vercel:marketplace","vercel:microfrontends","vercel:next-cache-components","vercel:next-forge","vercel:next-upgrade","vercel:nextjs","vercel:react-best-practices","vercel:routing-middleware","vercel:runtime-cache","vercel:shadcn","vercel:turbopack","vercel:vercel-agent","vercel:vercel-cli","vercel:vercel-firewall","vercel:vercel-functions","vercel:vercel-sandbox","vercel:vercel-storage","vercel:verification","vercel:workflow","update-config","keybindings-help","verify","code-review","simplify","fewer-permission-prompts","loop","schedule","claude-api","run","init","review","security-review"]},"type":"attachment","uuid":"ee4db023-aa02-4c46-b5d5-d79b18e7e410","timestamp":"2026-06-22T00:03:19.951Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"ee4db023-aa02-4c46-b5d5-d79b18e7e410","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01XJHWPF6dBuno3wWfGyKLmw","type":"message","role":"assistant","content":[{"type":"text","text":"I'll start by reading the original template file and checking the data export name."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":23595,"cache_creation_input_tokens":15098,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":15098,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKSPVR3kb4mD8CkBg4N","attributionAgent":"general-purpose","type":"assistant","uuid":"321fb983-6d69-4c57-b150-7e2e7caf5e37","timestamp":"2026-06-22T00:03:22.328Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"321fb983-6d69-4c57-b150-7e2e7caf5e37","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01XJHWPF6dBuno3wWfGyKLmw","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01VX5RxJBkDbfLvHu5Frd5ai","name":"Read","input":{"file_path":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/roof/RoofPageTemplate.tsx"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":23595,"cache_creation_input_tokens":15098,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":15098,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKSPVR3kb4mD8CkBg4N","attributionAgent":"general-purpose","type":"assistant","uuid":"a1528cbd-6429-4748-a883-6f3975648ecd","timestamp":"2026-06-22T00:03:23.301Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"a1528cbd-6429-4748-a883-6f3975648ecd","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01VX5RxJBkDbfLvHu5Frd5ai","type":"tool_result","content":"1\timport { useState } from 'react';\n2\timport {\n3\t  ArrowRight,\n4\t  BadgeCheck,\n5\t  CalendarClock,\n6\t  CalendarCheck,\n7\t  Check,\n8\t  CheckCircle2,\n9\t  ChevronDown,\n10\t  ClipboardCheck,\n11\t  Factory,\n12\t  HelpCircle,\n13\t  Play,\n14\t  ShieldCheck,\n15\t  ShieldAlert,\n16\t  Sparkles,\n17\t  SunMedium,\n18\t  Thermometer,\n19\t  TrendingDown,\n20\t  Users,\n21\t  Wrench,\n22\t  type LucideIcon,\n23\t} from 'lucide-react';\n24\t\n25\timport Footer from '@/components/Footer';\n26\timport Navbar from '@/components/Navbar';\n27\timport ScrollReveal from '@/components/ScrollReveal';\n28\timport StickyMobileCTA from '@/components/StickyMobileCTA';\n29\timport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';\n30\timport { Dialog, DialogContent } from '@/components/ui/dialog';\n31\timport { useJsonLd, usePageMeta } from '@/hooks/usePageMeta';\n32\timport type { RoofPageData } from '@/data/roofPages';\n33\timport coolRoofLayers from '@/assets/cool-roof-layers.webp';\n34\timport covaMetalProduct from '@/assets/produits/Covalba-CovaMetal20-Duo-Produit.webp';\n35\timport covaSealProduct from '@/assets/produits/Covalba-CovaSeal20-Duo-Produit.webp';\n36\timport covaTherm8Product from '@/assets/produits/Covalba-CovaTherm8-Produit.webp';\n37\timport covaTherm20Product from '@/assets/produits/Covalba-CovaTherm20-Produit.webp';\n38\t\n39\tinterface RoofPageTemplateProps {\n40\t  page: RoofPageData;\n41\t}\n42\t\n43\tconst SITE_URL = 'https://www.covalba.fr';\n44\t\n45\tconst cardIcons: LucideIcon[] = [SunMedium, Wrench, Factory, ShieldCheck];\n46\tconst methodIcons: LucideIcon[] = [ClipboardCheck, CalendarClock, ShieldCheck, TrendingDown];\n47\tconst proofIcons: LucideIcon[] = [Thermometer, ShieldCheck, BadgeCheck];\n48\tconst problemIcons: LucideIcon[] = [Users, SunMedium, ShieldAlert];\n49\t\n50\tconst logos = [\n51\t  { name: 'Continental', url: '/logos/logo-continental.svg' },\n52\t  { name: 'Thales', url: '/logos/logo-thales.svg' },\n53\t  { name: 'Nestlé', url: '/logos/logo-nestle.svg' },\n54\t  { name: 'SNCF', url: '/logos/logo-sncf.svg' },\n55\t  { name: 'Leclerc', url: '/logos/logo-leclerc.svg' },\n56\t  { name: 'Super U', url: '/logos/logo-super-u.svg' },\n57\t];\n58\t\n59\tconst displayCell = (value: string) => value.replace(/EUR/g, '€').replace(/m2/g, 'm²');\n60\t\n61\tconst formatCurrency = (value: number) =>\n62\t  `${new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 0 }).format(Math.round(value))} €`;\n63\t\n64\tconst parsePricePerM2 = (value: string) => {\n65\t  const match = displayCell(value).match(/\\(([\\d,.]+)\\s*€\\/m²\\)/);\n66\t  if (!match) return null;\n67\t\n68\t  return Number.parseFloat(match[1].replace(',', '.'));\n69\t};\n70\t\n71\tconst getTreatmentRange = (page: RoofPageData, situation: string) => {\n72\t  const text = `${page.slug} ${situation}`.toLowerCase();\n73\t\n74\t  if (text.includes('covaseal') || text.includes('vieillissante') || text.includes('fuite')) {\n75\t    return { min: 28, max: 38, label: 'CovaSeal 20 estimatif' };\n76\t  }\n77\t\n78\t  if (text.includes('covametal') || text.includes('corrosion') || text.includes('tôle')) {\n79\t    return { min: 24, max: 32, label: 'CovaMetal 20 estimatif' };\n80\t  }\n81\t\n82\t  if (text.includes('fibrociment') || text.includes('plaques')) {\n83\t    return { min: 22, max: 28, label: 'CovaTherm support sensible' };\n84\t  }\n85\t\n86\t  return { min: 20, max: 25, label: 'CovaTherm estimatif' };\n87\t};\n88\t\n89\tconst getFallbackPricePerM2 = (option: string, optionIndex: number, treatmentMidpoint: number) => {\n90\t  const lower = option.toLowerCase();\n91\t\n92\t  if (optionIndex === 0) return treatmentMidpoint;\n93\t  if (lower.includes('anticorrosion simple')) return 16;\n94\t  if (lower.includes('remplacement')) return 120;\n95\t  if (lower.includes('membrane neuve')) return 90;\n96\t  if (lower.includes('isolation')) return 150;\n97\t\n98\t  return null;\n99\t};\n100\t\n101\tconst benefitImages = [\n102\t  '/images/toitures/benefit-confort.webp',\n103\t  '/images/toitures/benefit-protection-toiture.webp',\n104\t  '/images/toitures/benefit-clim.webp',\n105\t  '/images/toitures/benefit-chantier-evite.webp',\n106\t];\n107\t\n108\tconst feedbackRoofBenefitImages = [\n109\t  '/images/toitures/feedback-batiment-tempere.png',\n110\t  '/images/toitures/feedback-etancheite.png',\n111\t  '/images/toitures/feedback-economie-clim-1.png',\n112\t  '/images/toitures/feedback-economie-clim-3.png',\n113\t];\n114\t\n115\tconst getBenefitImage = (page: RoofPageData, benefit: TextCard, index: number) => {\n116\t  if (['membrane-bitumineuse', 'toiture-plate'].includes(page.slug)) {\n117\t    return feedbackRoofBenefitImages[index] ?? feedbackRoofBenefitImages[0];\n118\t  }\n119\t\n120\t  const text = `${benefit.title} ${benefit.text}`.toLowerCase();\n121\t\n122\t  if (page.slug === 'bac-acier' && (text.includes('tôle') || text.includes('corrosion'))) {\n123\t    return '/images/toitures/support-bac-acier-cool.webp';\n124\t  }\n125\t\n126\t  if (text.includes('facture') || text.includes('clim') || text.includes('froid')) {\n127\t    return '/images/toitures/benefit-clim.webp';\n128\t  }\n129\t\n130\t  if (text.includes('chantier') || text.includes('réfection') || text.includes('remplacement') || text.includes('dépose')) {\n131\t    return '/images/toitures/benefit-chantier-evite.webp';\n132\t  }\n133\t\n134\t  if (text.includes('protég') || text.includes('étanchéité') || text.includes('corrosion') || text.includes('durée')) {\n135\t    return '/images/toitures/benefit-protection-toiture.webp';\n136\t  }\n137\t\n138\t  return benefitImages[index] ?? benefitImages[0];\n139\t};\n140\t\n141\tconst supportVisuals: Record<string, { hot: string; cool: string; hotLabel: string; coolLabel: string }> = {\n142\t  'membrane-bitumineuse': {\n143\t    hot: '/images/toitures/support-bitume-chaud.webp',\n144\t    cool: '/images/toitures/feedback-economie-clim-2.png',\n145\t    hotLabel: 'Sans cool roof : membrane bitume en absorption',\n146\t    coolLabel: 'Avec cool roof : membrane réfléchissante',\n147\t  },\n148\t  'bac-acier': {\n149\t    hot: '/images/toitures/support-bac-acier-chaud.webp',\n150\t    cool: '/images/toitures/support-bac-acier-cool.webp',\n151\t    hotLabel: 'Sans cool roof : métal en surchauffe',\n152\t    coolLabel: 'Avec cool roof : bac acier réfléchi et protégé',\n153\t  },\n154\t  'toiture-plate': {\n155\t    hot: '/images/toitures/support-toiture-plate-chaud.webp',\n156\t    cool: '/images/toitures/feedback-economie-clim-2.png',\n157\t    hotLabel: 'Sans cool roof : terrasse en absorption',\n158\t    coolLabel: 'Avec cool roof : terrasse réfléchissante',\n159\t  },\n160\t  fibrociment: {\n161\t    hot: '/images/toitures/support-fibrociment-chaud.webp',\n162\t    cool: '/images/toitures/support-fibrociment-cool.webp',\n163\t    hotLabel: 'Sans cool roof : fibrociment en absorption',\n164\t    coolLabel: 'Avec cool roof : plaques protégées',\n165\t  },\n166\t  'tuiles-ciment': {\n167\t    hot: '/images/toitures/support-tuiles-ciment-chaud.webp',\n168\t    cool: '/images/toitures/support-tuiles-ciment-cool.webp',\n169\t    hotLabel: 'Sans cool roof : tuiles qui stockent la chaleur',\n170\t    coolLabel: 'Avec cool roof : tuiles réfléchissantes',\n171\t  },\n172\t};\n173\t\n174\tconst layerTitles = [\"Primaire d'accroche\", 'Résine réfléchissante', 'Vernis de protection'];\n175\t\n176\tconst layerColors = [\n177\t  {\n178\t    pill: 'bg-terracotta text-white',\n179\t    dot: 'bg-terracotta',\n180\t    chevron: 'text-terracotta',\n181\t  },\n182\t  {\n183\t    pill: 'bg-foreground text-white',\n184\t    dot: 'bg-foreground',\n185\t    chevron: 'text-foreground',\n186\t  },\n187\t  {\n188\t    pill: 'bg-primary text-white',\n189\t    dot: 'bg-primary',\n190\t    chevron: 'text-primary',\n191\t  },\n192\t];\n193\t\n194\tconst layerPositions = [\n195\t  { top: '56.7%', left: '36.5%' },\n196\t  { top: '48.2%', left: '36.5%' },\n197\t  { top: '36.3%', left: '35.6%' },\n198\t];\n199\t\n200\tconst getProblemTitle = (title: string) => {\n201\t  const map: Record<string, { strong: string; muted: string }> = {\n202\t    \"Une toiture bitume noire, c'est un radiateur posé sur votre bâtiment.\": {\n203\t      strong: 'Votre toiture chauffe.',\n204\t      muted: 'Votre bâtiment encaisse.',\n205\t    },\n206\t    'Le bac acier chauffe vite, rouille vite, et fatigue vite.': {\n207\t      strong: 'Le métal chauffe vite.',\n208\t      muted: 'La toiture fatigue aussi.',\n209\t    },\n210\t    'À plat, votre toiture prend le soleil toute la journée.': {\n211\t      strong: 'Votre terrasse prend tout.',\n212\t      muted: \"Chaleur, UV, étanchéité.\",\n213\t    },\n214\t    'Le fibrociment chauffe, vieillit, et coûte cher à déposer.': {\n215\t      strong: 'Le fibrociment chauffe.',\n216\t      muted: 'Le déposer coûte cher.',\n217\t    },\n218\t    'Des tuiles ciment foncées, des combles en surchauffe.': {\n219\t      strong: 'Les tuiles stockent la chaleur.',\n220\t      muted: 'Les combles la subissent.',\n221\t    },\n222\t  };\n223\t\n224\t  return map[title] ?? { strong: title, muted: '' };\n225\t};\n226\t\n227\tconst getLayerDescription = (index: number) => {\n228\t  if (index === 0) {\n229\t    return \"S'adapte au support existant. Chaque toiture a sa chimie, chaque chimie demande une préparation et une accroche adaptées.\";\n230\t  }\n231\t\n232\t  if (index === 1) {\n233\t    return \"C'est le coeur du système. La surface réfléchit le rayonnement solaire au lieu de l'absorber, donc la chaleur rentre moins dans le bâtiment.\";\n234\t  }\n235\t\n236\t  return \"Un top coat protège la résine des UV, de la pollution et du vieillissement prématuré pour conserver la performance dans le temps.\";\n237\t};\n238\t\n239\tconst getLayerExpert = (index: number) => {\n240\t  const notes = [\n241\t    \"Un primaire inadapté, c'est un système qui cloque et se décolle. Le support décide de tout.\",\n242\t    \"Le cool roof traite la cause : la toiture chauffe moins, donc le bâtiment reçoit moins de calories.\",\n243\t    \"La différence entre une peinture et un investissement, c'est la tenue dans le temps. La finition est là pour protéger la performance.\",\n244\t  ];\n245\t\n246\t  return notes[index] ?? notes[0];\n247\t};\n248\t\n249\tconst Label = ({ children, light = false }: { children: string; light?: boolean }) => (\n250\t  <div className=\"mb-5 flex items-center gap-3\">\n251\t    <span className={`h-px w-9 ${light ? 'bg-teal-vivid/70' : 'bg-primary/65'}`} aria-hidden=\"true\" />\n252\t    <span\n253\t      className={`font-body text-[11px] font-bold uppercase tracking-[0.22em] ${\n254\t        light ? 'text-teal-vivid' : 'text-primary'\n255\t      }`}\n256\t    >\n257\t      {children}\n258\t    </span>\n259\t  </div>\n260\t);\n261\t\n262\tconst PrimaryCTA = ({ label, href }: { label: string; href: string }) => (\n263\t  <a\n264\t    href={href}\n265\t    className=\"group inline-flex items-center justify-center gap-2 rounded-full cta-gradient py-2.5 pl-6 pr-2 font-semibold text-white\"\n266\t  >\n267\t    <span className=\"text-sm\">{label}</span>\n268\t    <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n269\t      <ArrowRight className=\"h-3.5 w-3.5\" />\n270\t    </span>\n271\t  </a>\n272\t);\n273\t\n274\tconst RoofHero = ({ page }: { page: RoofPageData }) => {\n275\t  const [form, setForm] = useState({ climate: '', surface: '', email: '' });\n276\t  const [submitted, setSubmitted] = useState(false);\n277\t  const surfaceNum = parseFloat(form.surface) || 0;\n278\t  const estimate = surfaceNum ? `${new Intl.NumberFormat('fr-FR').format(surfaceNum * 22.5)} €` : '20-25 €/m²';\n279\t\n280\t  return (\n281\t    <section\n282\t      aria-labelledby={`${page.slug}-hero-title`}\n283\t      className=\"relative isolate flex min-h-[100svh] flex-col overflow-hidden bg-navy text-cream\"\n284\t    >\n285\t      <img\n286\t        src={page.heroImage}\n287\t        alt={page.heroImageAlt}\n288\t        className=\"absolute inset-0 -z-10 size-full scale-[1.02] object-cover\"\n289\t        style={page.slug === 'membrane-bitumineuse' ? { objectPosition: 'center 28%' } : undefined}\n290\t        loading=\"eager\"\n291\t        decoding=\"async\"\n292\t      />\n293\t      <div\n294\t        aria-hidden=\"true\"\n295\t        className=\"absolute inset-0 -z-10 hidden bg-gradient-to-r from-navy/[0.94] via-navy/62 to-navy/25 lg:block\"\n296\t      />\n297\t      <div\n298\t        aria-hidden=\"true\"\n299\t        className={`absolute inset-0 -z-10 bg-gradient-to-b lg:hidden ${\n300\t          page.slug === 'membrane-bitumineuse'\n301\t            ? 'from-navy/92 via-navy/58 to-navy/96'\n302\t            : 'from-navy/84 via-navy/38 to-navy/96'\n303\t        }`}\n304\t      />\n305\t      <div aria-hidden=\"true\" className=\"absolute inset-x-0 bottom-0 -z-10 h-[28%] bg-gradient-to-t from-navy via-navy/85 to-transparent\" />\n306\t\n307\t      <div className=\"container relative z-10 mx-auto flex flex-1 items-center px-6 pb-10 pt-24 lg:px-12 lg:pb-16 lg:pt-28\">\n308\t        <div className=\"grid w-full grid-cols-1 items-center gap-10 lg:grid-cols-[55fr_45fr] lg:gap-12\">\n309\t          <div className=\"max-w-2xl space-y-6 lg:space-y-8\">\n310\t            <ScrollReveal>\n311\t              <nav className=\"flex items-center gap-2 font-body text-xs text-cream/50\">\n312\t                <a href=\"/\" className=\"transition-colors hover:text-cream/75\">Accueil</a>\n313\t                <span className=\"text-cream/28\">/</span>\n314\t                <span>Toitures</span>\n315\t                <span className=\"text-cream/28\">/</span>\n316\t                <span className=\"text-cream/78\">{page.breadcrumb}</span>\n317\t              </nav>\n318\t            </ScrollReveal>\n319\t\n320\t            <ScrollReveal>\n321\t              <h1\n322\t                id={`${page.slug}-hero-title`}\n323\t                className=\"font-satoshi font-bold leading-[1.02] text-white\"\n324\t                style={{ fontSize: 'clamp(2.25rem, 4.6vw, 4rem)', letterSpacing: '-0.03em' }}\n325\t              >\n326\t                {page.h1}\n327\t              </h1>\n328\t            </ScrollReveal>\n329\t\n330\t            <ScrollReveal>\n331\t              <p\n332\t                className=\"max-w-xl font-satoshi font-medium leading-[1.3] text-cream/66\"\n333\t                style={{ fontSize: 'clamp(1rem, 1.5vw, 1.375rem)', letterSpacing: '-0.005em' }}\n334\t              >\n335\t                {page.lead}\n336\t              </p>\n337\t            </ScrollReveal>\n338\t\n339\t            <ScrollReveal>\n340\t              <ul className=\"hidden gap-4 pt-1 sm:flex sm:flex-wrap lg:gap-6\" aria-label=\"Bénéfices clés\">\n341\t                {page.heroStats.map((stat, index) => {\n342\t                  const Icon = [Thermometer, ShieldCheck, TrendingDown][index] ?? BadgeCheck;\n343\t                  return (\n344\t                    <li\n345\t                      key={stat.label}\n346\t                      className={`flex items-start gap-3 ${index !== 0 ? 'sm:border-l sm:border-white/10 sm:pl-6' : ''}`}\n347\t                    >\n348\t                      <Icon className=\"mt-0.5 h-[18px] w-[18px] shrink-0 text-teal-vivid\" strokeWidth={1.5} />\n349\t                      <div className=\"leading-tight\">\n350\t                        <p className=\"font-satoshi text-[15px] font-bold text-white lg:text-base\">{stat.value}</p>\n351\t                        <p className=\"mt-1 font-body text-[12.5px] text-cream/55 lg:text-[13px]\">{stat.label}</p>\n352\t                      </div>\n353\t                    </li>\n354\t                  );\n355\t                })}\n356\t              </ul>\n357\t            </ScrollReveal>\n358\t          </div>\n359\t\n360\t          <ScrollReveal>\n361\t            <div className=\"rounded-[1.5rem] border border-white/[0.08] bg-white/[0.04] p-1.5 backdrop-blur-sm\">\n362\t              <div className=\"rounded-[calc(1.5rem-6px)] border border-white/[0.06] bg-[#0b1a2b]/80 p-4 shadow-[inset_0_1px_1px_rgba(255,255,255,0.06)] backdrop-blur-xl lg:p-7\">\n363\t                {!submitted ? (\n364\t                  <>\n365\t                    <h2 className=\"mb-1 text-center font-satoshi text-xl font-bold text-white lg:text-2xl\">\n366\t                      Estimez le gain possible\n367\t                    </h2>\n368\t                    <p className=\"mb-4 text-center font-body text-[12px] text-white/50\">\n369\t                      Réponse personnalisée sous 24h\n370\t                    </p>\n371\t                    <form\n372\t                      onSubmit={(event) => {\n373\t                        event.preventDefault();\n374\t                        setSubmitted(true);\n375\t                      }}\n376\t                      className=\"space-y-2.5 lg:space-y-3\"\n377\t                    >\n378\t                      <input\n379\t                        value={page.breadcrumb}\n380\t                        readOnly\n381\t                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white outline-none\"\n382\t                      />\n383\t                      <select\n384\t                        required\n385\t                        value={form.climate}\n386\t                        onChange={(e) => setForm({ ...form, climate: e.target.value })}\n387\t                        className=\"w-full appearance-none rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n388\t                      >\n389\t                        <option value=\"\" disabled className=\"bg-card text-foreground\">Bâtiment climatisé ?</option>\n390\t                        {['Oui', 'Non', 'Partiellement', 'À confirmer'].map((option) => (\n391\t                          <option key={option} value={option} className=\"bg-card text-foreground\">{option}</option>\n392\t                        ))}\n393\t                      </select>\n394\t                      <input\n395\t                        type=\"number\"\n396\t                        placeholder=\"Surface toiture (m²)\"\n397\t                        required\n398\t                        min=\"1\"\n399\t                        value={form.surface}\n400\t                        onChange={(e) => setForm({ ...form, surface: e.target.value })}\n401\t                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n402\t                      />\n403\t                      <input\n404\t                        type=\"email\"\n405\t                        placeholder=\"Email professionnel\"\n406\t                        required\n407\t                        value={form.email}\n408\t                        onChange={(e) => setForm({ ...form, email: e.target.value })}\n409\t                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n410\t                      />\n411\t\n412\t                      <button\n413\t                        type=\"submit\"\n414\t                        className=\"group mt-1 flex w-full items-center justify-center gap-3 rounded-full cta-gradient py-3 font-semibold text-white active:scale-[0.98]\"\n415\t                      >\n416\t                        <span className=\"text-[15px]\">Voir mon potentiel</span>\n417\t                        <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n418\t                          <ArrowRight className=\"h-4 w-4\" />\n419\t                        </span>\n420\t                      </button>\n421\t                    </form>\n422\t\n423\t                    <p className=\"mt-3 text-center font-body text-[11px] text-white/35\">\n424\t                      Sans engagement · Étude adaptée au support\n425\t                    </p>\n426\t                  </>\n427\t                ) : (\n428\t                  <div className=\"py-4 text-center\">\n429\t                    <div className=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary/20\">\n430\t                      <ShieldCheck className=\"h-6 w-6 text-primary\" />\n431\t                    </div>\n432\t                    <p className=\"mb-1 font-body text-sm text-white/60\">Base indicative pour {new Intl.NumberFormat('fr-FR').format(surfaceNum)} m²</p>\n433\t                    <p className=\"mb-4 font-satoshi text-2xl font-bold text-white\">{estimate}</p>\n434\t                    <p className=\"mb-4 font-body text-[13px] text-white/50\">\n435\t                      Un conseiller affine le chiffrage selon l'état réel de la toiture.\n436\t                    </p>\n437\t                    <button onClick={() => setSubmitted(false)} className=\"font-body text-sm font-semibold text-primary hover:underline\">\n438\t                      Modifier ma demande\n439\t                    </button>\n440\t                  </div>\n441\t                )}\n442\t              </div>\n443\t            </div>\n444\t          </ScrollReveal>\n445\t        </div>\n446\t      </div>\n447\t\n448\t      <div className=\"relative z-10 hidden border-t border-white/10 bg-navy/78 backdrop-blur-md lg:block\">\n449\t        <div className=\"container mx-auto flex items-center gap-8 px-12 py-5\">\n450\t          <p className=\"shrink-0 font-body text-[10px] font-semibold uppercase tracking-[0.24em] text-cream/45\">\n451\t            Ils nous font confiance\n452\t          </p>\n453\t          <div className=\"flex flex-1 items-center justify-between gap-8\">\n454\t            {logos.map((logo) => (\n455\t              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n456\t                <img\n457\t                  src={logo.url}\n458\t                  alt={logo.name}\n459\t                  className=\"max-h-7 max-w-full object-contain opacity-65 brightness-0 invert transition-opacity duration-300 hover:opacity-95\"\n460\t                  loading=\"eager\"\n461\t                  decoding=\"async\"\n462\t                />\n463\t              </span>\n464\t            ))}\n465\t          </div>\n466\t        </div>\n467\t      </div>\n468\t    </section>\n469\t  );\n470\t};\n471\t\n472\tconst ProblemSection = ({ page }: { page: RoofPageData }) => {\n473\t  const title = getProblemTitle(page.problem.title);\n474\t  const visual = supportVisuals[page.slug] ?? supportVisuals['membrane-bitumineuse'];\n475\t\n476\t  return (\n477\t    <section className=\"relative overflow-hidden bg-cream py-16 lg:py-36\">\n478\t      <div className=\"container relative mx-auto px-4 lg:px-8\">\n479\t        <ScrollReveal>\n480\t          <div className=\"mx-auto mb-14 max-w-3xl text-center lg:mb-20\">\n481\t            <h2\n482\t              className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n483\t              style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n484\t            >\n485\t              {title.strong}\n486\t              {title.muted && <span className=\"text-foreground/30\"> {title.muted}</span>}\n487\t            </h2>\n488\t          </div>\n489\t        </ScrollReveal>\n490\t\n491\t        <div className=\"grid grid-cols-1 items-center gap-12 lg:grid-cols-12 lg:gap-16\">\n492\t          <div className=\"order-2 lg:order-1 lg:col-span-5\">\n493\t            <ScrollReveal>\n494\t              <div className=\"relative\">\n495\t                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_hsl(var(--terracotta)/0.18)_0%,_transparent_65%)] blur-2xl\" />\n496\t                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_hsl(var(--terracotta-dark)/0.25),_0_10px_30px_-5px_hsl(var(--terracotta)/0.12)]\">\n497\t                  <img\n498\t                    src={visual.hot}\n499\t                    alt=\"\"\n500\t                    className=\"h-full w-full object-contain p-4\"\n501\t                    loading=\"lazy\"\n502\t                    decoding=\"async\"\n503\t                  />\n504\t                </div>\n505\t              </div>\n506\t              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-terracotta/60\">\n507\t                {visual.hotLabel}\n508\t              </p>\n509\t            </ScrollReveal>\n510\t          </div>\n511\t\n512\t          <div className=\"order-1 lg:order-2 lg:col-span-7 lg:pl-12\">\n513\t            <ScrollReveal stagger>\n514\t              <div\n515\t                className=\"space-y-8 border-l-2 pl-8 lg:space-y-10 lg:pl-10\"\n516\t                style={{ borderImage: 'linear-gradient(to bottom, hsl(var(--terracotta)), hsl(var(--terracotta-dark))) 1' }}\n517\t              >\n518\t                {page.problem.cards.map((card, index) => {\n519\t                  const Icon = problemIcons[index] ?? HelpCircle;\n520\t                  return (\n521\t                    <div key={card.title}>\n522\t                      <div className=\"mb-2 flex items-center gap-3\">\n523\t                        <Icon className=\"h-5 w-5 text-terracotta\" strokeWidth={1.8} />\n524\t                        <h3\n525\t                          className=\"font-satoshi text-xl font-bold text-foreground lg:text-2xl\"\n526\t                          style={{ letterSpacing: '-0.02em', lineHeight: 1.2 }}\n527\t                        >\n528\t                          {card.title}\n529\t                        </h3>\n530\t                      </div>\n531\t                      <p className=\"font-body text-base leading-relaxed text-foreground/55\">{card.text}</p>\n532\t                    </div>\n533\t                  );\n534\t                })}\n535\t              </div>\n536\t            </ScrollReveal>\n537\t          </div>\n538\t        </div>\n539\t\n540\t        <div className=\"mt-20 grid grid-cols-1 items-center gap-12 lg:mt-28 lg:grid-cols-12 lg:gap-16\">\n541\t          <div className=\"order-2 lg:order-2 lg:col-span-5\">\n542\t            <ScrollReveal>\n543\t              <div className=\"relative\">\n544\t                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_rgba(32,150,140,0.18)_0%,_transparent_65%)] blur-2xl\" />\n545\t                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_rgba(20,120,110,0.20),_0_10px_30px_-5px_rgba(32,150,140,0.10)]\">\n546\t                  <img\n547\t                    src={visual.cool}\n548\t                    alt=\"\"\n549\t                    className=\"h-full w-full object-contain p-4\"\n550\t                    loading=\"lazy\"\n551\t                    decoding=\"async\"\n552\t                  />\n553\t                </div>\n554\t              </div>\n555\t              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-primary/60\">\n556\t                {visual.coolLabel}\n557\t              </p>\n558\t            </ScrollReveal>\n559\t          </div>\n560\t\n561\t          <div className=\"order-1 lg:order-1 lg:col-span-7 lg:pr-12\">\n562\t            <ScrollReveal>\n563\t              <h3\n564\t                className=\"mb-8 max-w-xl font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n565\t                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n566\t              >\n567\t                Le cool roof : une réponse concrète\n568\t                <span className=\"text-foreground/30\"> au problème de chaleur.</span>\n569\t              </h3>\n570\t              <p className=\"mb-8 max-w-lg font-body text-base leading-relaxed text-foreground/50\">\n571\t                Une surface claire renvoie le rayonnement solaire avant qu'il ne pénètre dans le bâtiment. La toiture chauffe moins, les derniers niveaux respirent, et le support subit moins d'UV et de chocs thermiques.\n572\t              </p>\n573\t              <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n574\t            </ScrollReveal>\n575\t          </div>\n576\t        </div>\n577\t      </div>\n578\t    </section>\n579\t  );\n580\t};\n581\t\n582\tconst SolutionSection = ({ page }: { page: RoofPageData }) => {\n583\t  const [active, setActive] = useState(0);\n584\t\n585\t  return (\n586\t    <section className=\"relative bg-white\">\n587\t      <div className=\"container mx-auto px-4 lg:px-8\">\n588\t        <ScrollReveal>\n589\t          <div className=\"flex flex-col gap-8 pb-10 pt-16 lg:flex-row lg:items-end lg:gap-16 lg:pb-16 lg:pt-32\">\n590\t            <div className=\"lg:w-2/3\">\n591\t              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-muted-foreground\">\n592\t                {page.solution.badge}\n593\t              </p>\n594\t              <h2\n595\t                className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n596\t                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n597\t              >\n598\t                <span className=\"text-foreground/30\">Un système conçu pour </span>\n599\t                réfléchir, protéger et durer.\n600\t              </h2>\n601\t            </div>\n602\t            <div className=\"lg:w-1/3 lg:pb-1\">\n603\t              <p className=\"font-body text-base leading-relaxed text-foreground/52 lg:text-right lg:text-lg\">\n604\t                Un primaire, une résine, un vernis qui garantissent la performance de votre toiture dans le temps.\n605\t              </p>\n606\t              <div className=\"mt-5 flex lg:justify-end\">\n607\t                <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n608\t              </div>\n609\t            </div>\n610\t          </div>\n611\t        </ScrollReveal>\n612\t\n613\t        <div className=\"flex flex-col gap-12 pb-24 lg:flex-row lg:gap-16 lg:pb-32\">\n614\t          <div className=\"overflow-hidden rounded-2xl shadow-[0_20px_60px_-15px_rgba(0,0,0,0.08)] lg:hidden\">\n615\t            <img src={coolRoofLayers} alt=\"Coupe du système cool roof Covalba\" className=\"w-full\" loading=\"lazy\" />\n616\t          </div>\n617\t\n618\t          <div className=\"lg:w-1/2 lg:sticky lg:top-[20vh] lg:self-start\">\n619\t            {layerTitles.map((item, index) => {\n620\t              const isOpen = active === index;\n621\t              const colors = layerColors[index] ?? layerColors[0];\n622\t              return (\n623\t                <div key={item} className=\"border-b border-border last:border-b-0\">\n624\t                  <button\n625\t                    onClick={() => setActive(index)}\n626\t                    className=\"group flex w-full items-center gap-4 py-6 text-left\"\n627\t                  >\n628\t                    <span\n629\t                      className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full font-body text-[11px] font-bold transition-all duration-300 ${\n630\t                        isOpen ? colors.pill : 'bg-muted text-muted-foreground'\n631\t                      }`}\n632\t                    >\n633\t                      0{index + 1}\n634\t                    </span>\n635\t                    <h3\n636\t                      className={`flex-1 font-satoshi text-xl font-bold transition-colors duration-300 lg:text-2xl ${\n637\t                        isOpen ? 'text-foreground' : 'text-foreground/50 group-hover:text-foreground/80'\n638\t                      }`}\n639\t                    >\n640\t                      {item}\n641\t                    </h3>\n642\t                    <ChevronDown\n643\t                      className={`h-5 w-5 shrink-0 transition-all duration-300 ${\n644\t                        isOpen ? `${colors.chevron} rotate-180` : 'text-foreground/25'\n645\t                      }`}\n646\t                      strokeWidth={2}\n647\t                    />\n648\t                  </button>\n649\t\n650\t                  <div\n651\t                    className={`grid transition-all duration-500 ${\n652\t                      isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'\n653\t                    }`}\n654\t                  >\n655\t                    <div className=\"overflow-hidden\">\n656\t                      <div className=\"pb-8 pl-6 lg:pl-12\">\n657\t                        <p className=\"mb-6 font-body text-base leading-relaxed text-foreground/65 lg:text-lg\">\n658\t                          {getLayerDescription(index)}\n659\t                        </p>\n660\t                        <div className=\"flex items-start gap-3 rounded-xl bg-foreground/[0.03] p-4\">\n661\t                          <img\n662\t                            src=\"/maxime-bourassin.jpg\"\n663\t                            alt=\"Maxime Bourassin, fondateur Covalba\"\n664\t                            className=\"mt-0.5 h-9 w-9 shrink-0 rounded-full object-cover\"\n665\t                          />\n666\t                          <div>\n667\t                            <p className=\"font-body text-[15px] italic leading-relaxed text-foreground/65 lg:text-base\">\n668\t                              \"{getLayerExpert(index)}\"\n669\t                            </p>\n670\t                            <p className=\"mt-2 font-body text-xs font-semibold text-foreground/50\">\n671\t                              Maxime Bourassin, fondateur\n672\t                            </p>\n673\t                          </div>\n674\t                        </div>\n675\t                        {index === 0 && page.solution.note && (\n676\t                          <p className=\"mt-4 rounded-xl bg-primary/5 p-4 font-body text-sm leading-relaxed text-foreground/62\">\n677\t                            {page.solution.note}\n678\t                          </p>\n679\t                        )}\n680\t                      </div>\n681\t                    </div>\n682\t                  </div>\n683\t                </div>\n684\t              );\n685\t            })}\n686\t          </div>\n687\t\n688\t          <div className=\"hidden lg:block lg:w-1/2\">\n689\t            <div className=\"relative overflow-hidden rounded-2xl shadow-[0_30px_80px_-15px_rgba(0,0,0,0.08)]\">\n690\t              <img src={coolRoofLayers} alt=\"Coupe du système cool roof Covalba\" className=\"w-full\" loading=\"lazy\" />\n691\t              {layerTitles.map((item, index) => {\n692\t                const pos = layerPositions[index];\n693\t                const colors = layerColors[index] ?? layerColors[0];\n694\t                const isActive = active === index;\n695\t                if (!pos) return null;\n696\t\n697\t                return (\n698\t                  <div key={item} className=\"absolute\" style={{ top: pos.top, left: pos.left }}>\n699\t                    <div\n700\t                      className={`absolute -translate-x-1/2 -translate-y-1/2 transition-all duration-500 ${\n701\t                        isActive ? 'scale-125' : 'scale-75 opacity-40'\n702\t                      }`}\n703\t                    >\n704\t                      <div\n705\t                        className={`h-3.5 w-3.5 rounded-full ${\n706\t                          isActive ? colors.dot : 'bg-foreground/20'\n707\t                        } shadow-lg ring-2 ring-white transition-colors duration-500`}\n708\t                      />\n709\t                      {isActive && (\n710\t                        <div className={`absolute inset-0 h-3.5 w-3.5 animate-ping rounded-full ${colors.dot} opacity-30`} />\n711\t                      )}\n712\t                    </div>\n713\t                    <div\n714\t                      className={`pointer-events-none absolute right-5 top-1/2 -translate-y-1/2 transition-all duration-500 ${\n715\t                        isActive ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-0'\n716\t                      }`}\n717\t                    >\n718\t                      <span className={`whitespace-nowrap rounded-full px-4 py-1.5 font-body text-[13px] font-bold shadow-lg ${colors.pill}`}>\n719\t                        {item}\n720\t                      </span>\n721\t                    </div>\n722\t                  </div>\n723\t                );\n724\t              })}\n725\t            </div>\n726\t          </div>\n727\t        </div>\n728\t      </div>\n729\t    </section>\n730\t  );\n731\t};\n732\t\n733\tconst BenefitsSection = ({ page }: { page: RoofPageData }) => (\n734\t  <section className=\"bg-white py-20 lg:py-32\">\n735\t    <div className=\"container mx-auto px-4 lg:px-8\">\n736\t      <ScrollReveal>\n737\t        <div className=\"mb-10 grid gap-8 lg:grid-cols-[0.82fr_1.18fr] lg:items-end lg:gap-14\">\n738\t          <div>\n739\t            <Label>{page.benefits.badge}</Label>\n740\t            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n741\t              {page.benefits.title}\n742\t            </h2>\n743\t          </div>\n744\t          <p className=\"max-w-xl border-l border-primary/30 pl-6 font-body text-base leading-relaxed text-foreground/62 lg:text-lg\">\n745\t            {page.benefits.featured}\n746\t          </p>\n747\t        </div>\n748\t      </ScrollReveal>\n749\t\n750\t      <ScrollReveal stagger>\n751\t        <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n752\t          {page.benefits.cards.map((benefit, index) => {\n753\t            const Icon = cardIcons[index] ?? Sparkles;\n754\t            return (\n755\t              <article\n756\t                key={benefit.title}\n757\t                className=\"group overflow-hidden rounded-2xl bg-cream ring-1 ring-foreground/[0.05] transition-all duration-500 hover:-translate-y-1 hover:shadow-[0_24px_70px_-48px_hsl(var(--navy)/0.45)]\"\n758\t              >\n759\t                <div className=\"relative aspect-[4/3] overflow-hidden bg-foreground/[0.04]\">\n760\t                  <img\n761\t                    src={getBenefitImage(page, benefit, index)}\n762\t                    alt=\"\"\n763\t                    className=\"h-full w-full object-cover transition-transform duration-700 group-hover:scale-[1.035]\"\n764\t                    loading=\"lazy\"\n765\t                    decoding=\"async\"\n766\t                  />\n767\t                  <div className=\"absolute inset-x-0 bottom-0 h-20 bg-gradient-to-t from-cream to-transparent\" />\n768\t                </div>\n769\t                <div className=\"p-5 lg:p-6\">\n770\t                  <div className=\"mb-4 flex h-10 w-10 items-center justify-center rounded-xl bg-white text-primary shadow-[0_12px_30px_-24px_hsl(var(--navy)/0.35)]\">\n771\t                    <Icon className=\"h-5 w-5\" strokeWidth={1.7} />\n772\t                  </div>\n773\t                  <h3 className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{benefit.title}</h3>\n774\t                  <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/62\">{benefit.text}</p>\n775\t                </div>\n776\t              </article>\n777\t            );\n778\t          })}\n779\t        </div>\n780\t      </ScrollReveal>\n781\t    </div>\n782\t  </section>\n783\t);\n784\t\n785\tconst RoiOptionCards = ({\n786\t  page,\n787\t  surface,\n788\t  treatmentRange,\n789\t}: {\n790\t  page: RoofPageData;\n791\t  surface: number;\n792\t  treatmentRange: ReturnType<typeof getTreatmentRange>;\n793\t}) => {\n794\t  if (!page.roi.comparison) {\n795\t    return page.roi.afterComparison ? (\n796\t      <article className=\"rounded-2xl bg-white p-6 ring-1 ring-foreground/[0.06] lg:p-8\">\n797\t        <p className=\"font-body text-base leading-relaxed text-foreground/66 lg:text-lg\">\n798\t          {page.roi.afterComparison}\n799\t        </p>\n800\t      </article>\n801\t    ) : null;\n802\t  }\n803\t\n804\t  const options = page.roi.comparison.headers.slice(1);\n805\t  const rows = page.roi.comparison.rows;\n806\t  const investment = rows.find((row) => row[0].toLowerCase().includes('investissement'));\n807\t  const duration = rows.find((row) => row[0].toLowerCase().includes('dur'));\n808\t  const activity = rows.find((row) => row[0].toLowerCase().includes('activ'));\n809\t  const treatmentMidpoint = (treatmentRange.min + treatmentRange.max) / 2;\n810\t\n811\t  return (\n812\t    <div className=\"grid gap-4 lg:grid-cols-3\">\n813\t      {options.map((option, optionIndex) => {\n814\t        const isCovalba = optionIndex === 0;\n815\t        const rawInvestment = investment?.[optionIndex + 1] ?? '';\n816\t        const parsedPrice = parsePricePerM2(rawInvestment);\n817\t        const pricePerM2 = parsedPrice ?? getFallbackPricePerM2(option, optionIndex, treatmentMidpoint);\n818\t        const dynamicInvestment = pricePerM2 ? `~${formatCurrency(surface * pricePerM2)}` : displayCell(rawInvestment);\n819\t\n820\t        return (\n821\t          <article\n822\t            key={option}\n823\t            className={`rounded-2xl p-6 ring-1 ${\n824\t              isCovalba\n825\t                ? 'bg-[#142333] text-white ring-transparent'\n826\t                : 'bg-white text-foreground ring-foreground/[0.06]'\n827\t            }`}\n828\t          >\n829\t            <div className=\"mb-6 flex items-start justify-between gap-4\">\n830\t              <h3 className=\"font-satoshi text-xl font-black leading-tight\">{option}</h3>\n831\t              {isCovalba && (\n832\t                <span className=\"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-teal-vivid/15 text-teal-vivid\">\n833\t                  <Check className=\"h-4 w-4\" strokeWidth={2.4} />\n834\t                </span>\n835\t              )}\n836\t            </div>\n837\t            <p className={`font-satoshi text-3xl font-black leading-none ${isCovalba ? 'text-teal-vivid' : 'text-terracotta'}`}>\n838\t              {dynamicInvestment}\n839\t            </p>\n840\t            {pricePerM2 && (\n841\t              <p className={`mt-2 font-body text-xs font-semibold ${isCovalba ? 'text-white/45' : 'text-foreground/40'}`}>\n842\t                base {new Intl.NumberFormat('fr-FR').format(surface)} m² · {pricePerM2.toLocaleString('fr-FR')} €/m²\n843\t              </p>\n844\t            )}\n845\t            <dl className=\"mt-6 space-y-3 border-t border-current/10 pt-5\">\n846\t              {[duration, activity].filter(Boolean).map((row) => (\n847\t                <div key={row![0]} className=\"grid grid-cols-[0.75fr_1fr] gap-4\">\n848\t                  <dt className={`font-body text-xs font-bold uppercase tracking-[0.14em] ${isCovalba ? 'text-white/38' : 'text-foreground/35'}`}>\n849\t                    {displayCell(row![0])}\n850\t                  </dt>\n851\t                  <dd className={`font-body text-sm leading-relaxed ${isCovalba ? 'text-white/74' : 'text-foreground/62'}`}>\n852\t                    {displayCell(row![optionIndex + 1])}\n853\t                  </dd>\n854\t                </div>\n855\t              ))}\n856\t            </dl>\n857\t          </article>\n858\t        );\n859\t      })}\n860\t    </div>\n861\t  );\n862\t};\n863\t\n864\tconst ROICalculator = ({\n865\t  page,\n866\t  surface,\n867\t  selectedSituation,\n868\t  treatmentRange,\n869\t  onSurfaceChange,\n870\t  onSituationChange,\n871\t}: {\n872\t  page: RoofPageData;\n873\t  surface: number;\n874\t  selectedSituation: string;\n875\t  treatmentRange: ReturnType<typeof getTreatmentRange>;\n876\t  onSurfaceChange: (surface: number) => void;\n877\t  onSituationChange: (situation: string) => void;\n878\t}) => {\n879\t  const treatmentLow = surface * treatmentRange.min;\n880\t  const treatmentHigh = surface * treatmentRange.max;\n881\t  const referencePrice = selectedSituation.toLowerCase().includes('isolation') ? 150 : 90;\n882\t  const referenceCost = surface * referencePrice;\n883\t  const potentialGap = Math.max(referenceCost - treatmentHigh, 0);\n884\t\n885\t  return (\n886\t    <div className=\"mb-10 rounded-[2rem] bg-[#142333] p-5 text-white shadow-[0_34px_90px_-64px_hsl(var(--navy)/0.65)] lg:p-8\">\n887\t      <div className=\"grid gap-7 lg:grid-cols-[0.88fr_1.12fr] lg:items-end\">\n888\t        <div>\n889\t          <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-teal-vivid/70\">\n890\t            Calculateur indicatif\n891\t          </p>\n892\t          <h3 className=\"mt-4 max-w-lg font-satoshi text-2xl font-black leading-tight lg:text-3xl\">\n893\t            Ajustez la surface et l'état de la toiture.\n894\t          </h3>\n895\t          <p className=\"mt-3 max-w-lg font-body text-sm leading-relaxed text-white/52\">\n896\t            Par défaut : 5 000 m², surface type d'un entrepôt ou bâtiment industriel à traiter.\n897\t          </p>\n898\t        </div>\n899\t\n900\t        <div className=\"grid gap-3 sm:grid-cols-[0.72fr_1.28fr]\">\n901\t          <label className=\"block\">\n902\t            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n903\t              Surface\n904\t            </span>\n905\t            <input\n906\t              type=\"number\"\n907\t              min={100}\n908\t              step={100}\n909\t              value={surface}\n910\t              onChange={(event) => onSurfaceChange(Math.max(100, Number(event.target.value) || 100))}\n911\t              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n912\t              aria-label=\"Surface à traiter en mètres carrés\"\n913\t            />\n914\t          </label>\n915\t          <label className=\"block\">\n916\t            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n917\t              Situation\n918\t            </span>\n919\t            <select\n920\t              value={selectedSituation}\n921\t              onChange={(event) => onSituationChange(event.target.value)}\n922\t              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n923\t              aria-label=\"Situation de la toiture\"\n924\t            >\n925\t              {page.roi.situation.rows.map((row) => (\n926\t                <option key={row[0]} value={row[0]} className=\"bg-card text-foreground\">\n927\t                  {row[0]}\n928\t                </option>\n929\t              ))}\n930\t            </select>\n931\t          </label>\n932\t        </div>\n933\t      </div>\n934\t\n935\t      <div className=\"mt-7 grid gap-3 md:grid-cols-3\">\n936\t        {[\n937\t          {\n938\t            label: treatmentRange.label,\n939\t            value: `${formatCurrency(treatmentLow)} - ${formatCurrency(treatmentHigh)}`,\n940\t            detail: `${treatmentRange.min}-${treatmentRange.max} €/m² selon préparation`,\n941\t            valueClass: 'text-teal-vivid',\n942\t          },\n943\t          {\n944\t            label: 'Réfection de référence',\n945\t            value: `~${formatCurrency(referenceCost)}`,\n946\t            detail: `${referencePrice} €/m², ordre de grandeur à comparer`,\n947\t            valueClass: 'text-terracotta',\n948\t          },\n949\t          {\n950\t            label: 'Écart potentiel',\n951\t            value: `jusqu'à ${formatCurrency(potentialGap)}`,\n952\t            detail: \"Investissement lourd évité ou repoussé si le support est viable\",\n953\t            valueClass: 'text-teal-vivid',\n954\t          },\n955\t        ].map((item) => (\n956\t          <article key={item.label} className=\"rounded-2xl bg-white/[0.07] p-5 ring-1 ring-white/10\">\n957\t            <p className=\"font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/38\">{item.label}</p>\n958\t            <p className={`mt-3 font-satoshi text-2xl font-black leading-tight ${item.valueClass}`}>{item.value}</p>\n959\t            <p className=\"mt-2 font-body text-xs leading-relaxed text-white/45\">{item.detail}</p>\n960\t          </article>\n961\t        ))}\n962\t      </div>\n963\t      <p className=\"mt-4 font-body text-xs leading-relaxed text-white/38\">\n964\t        Chiffrage indicatif : l'étude ajuste le prix selon l'accès, la préparation, les points singuliers et l'état réel du support.\n965\t      </p>\n966\t    </div>\n967\t  );\n968\t};\n969\t\n970\tconst getSituationImage = (recommendation: string) => {\n971\t  const lower = recommendation.toLowerCase();\n972\t  if (lower.includes('covametal')) return covaMetalProduct;\n973\t  if (lower.includes('covaseal')) return covaSealProduct;\n974\t  if (lower.includes('cova')) return covaTherm20Product;\n975\t  return covaTherm8Product;\n976\t};\n977\t\n978\tconst SituationCards = ({ rows }: { rows: string[][] }) => (\n979\t  <div className=\"grid gap-4 md:grid-cols-2\">\n980\t    {rows.map((row) => (\n981\t      <article key={row[0]} className=\"grid gap-4 rounded-2xl bg-white p-4 ring-1 ring-foreground/[0.06] sm:grid-cols-[128px_1fr] lg:grid-cols-[190px_1fr] lg:gap-5 lg:p-5\">\n982\t        <div className=\"flex aspect-[4/3] items-center justify-center rounded-xl bg-gradient-to-br from-cream to-white p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.75),_0_18px_44px_-34px_hsl(var(--navy)/0.45)] ring-1 ring-foreground/[0.04] sm:aspect-square lg:p-6\">\n983\t          <img src={getSituationImage(row[1])} alt=\"\" className=\"max-h-full max-w-full object-contain\" loading=\"lazy\" decoding=\"async\" />\n984\t        </div>\n985\t        <div className=\"self-center\">\n986\t          <p className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{row[0]}</p>\n987\t          <p className=\"mt-2 font-body text-sm font-bold text-primary\">{row[1]}</p>\n988\t          <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/58\">{row[2]}</p>\n989\t        </div>\n990\t      </article>\n991\t    ))}\n992\t  </div>\n993\t);\n994\t\n995\tconst ROISection = ({ page }: { page: RoofPageData }) => {\n996\t  const [surface, setSurface] = useState(5000);\n997\t  const [selectedSituation, setSelectedSituation] = useState(page.roi.situation.rows[0]?.[0] ?? '');\n998\t  const treatmentRange = getTreatmentRange(page, selectedSituation);\n999\t\n1000\t  return (\n1001\t    <section className=\"bg-cream py-20 lg:py-32\">\n1002\t      <div className=\"container mx-auto px-4 lg:px-8\">\n1003\t      <ScrollReveal>\n1004\t        <div className=\"mx-auto mb-12 max-w-3xl text-center lg:mb-16\">\n1005\t          <div className=\"flex justify-center\">\n1006\t            <Label>{page.roi.badge}</Label>\n1007\t          </div>\n1008\t          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-[3rem]\">\n1009\t            {page.roi.title}\n1010\t          </h2>\n1011\t          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-foreground/58 lg:text-lg\">\n1012\t            {page.roi.intro}\n1013\t          </p>\n1014\t        </div>\n1015\t      </ScrollReveal>\n1016\t\n1017\t      <ScrollReveal>\n1018\t        <div className=\"mb-12 grid gap-4 md:grid-cols-3\">\n1019\t          {[\n1020\t            ['20-25 €/m²', 'pour traiter et rafraîchir'],\n1021\t            ['8-10 ans', 'de réfection repoussée'],\n1022\t            ['0 arrêt', \"d'activité pendant le chantier\"],\n1023\t          ].map(([value, label]) => (\n1024\t            <article key={value} className=\"rounded-2xl bg-white p-6 text-center ring-1 ring-foreground/[0.06]\">\n1025\t              <p className=\"font-satoshi text-4xl font-black leading-none text-primary\">{value}</p>\n1026\t              <p className=\"mt-3 font-body text-sm font-semibold text-foreground/52\">{label}</p>\n1027\t            </article>\n1028\t          ))}\n1029\t        </div>\n1030\t      </ScrollReveal>\n1031\t\n1032\t      <ScrollReveal>\n1033\t        <div className=\"mb-8 max-w-2xl\">\n1034\t          <div>\n1035\t            <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n1036\t              Comparer les options\n1037\t            </p>\n1038\t            <h3 className=\"mt-4 font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n1039\t              Une lecture rapide du budget.\n1040\t            </h3>\n1041\t          </div>\n1042\t        </div>\n1043\t      </ScrollReveal>\n1044\t\n1045\t      <ScrollReveal>\n1046\t        <ROICalculator\n1047\t          page={page}\n1048\t          surface={surface}\n1049\t          selectedSituation={selectedSituation}\n1050\t          treatmentRange={treatmentRange}\n1051\t          onSurfaceChange={setSurface}\n1052\t          onSituationChange={setSelectedSituation}\n1053\t        />\n1054\t      </ScrollReveal>\n1055\t\n1056\t      <ScrollReveal>\n1057\t        <RoiOptionCards page={page} surface={surface} treatmentRange={treatmentRange} />\n1058\t        {page.roi.comparison && page.roi.afterComparison && (\n1059\t          <p className=\"mt-5 max-w-3xl font-body text-sm leading-relaxed text-foreground/52 lg:text-base\">\n1060\t            {page.roi.afterComparison}\n1061\t          </p>\n1062\t        )}\n1063\t      </ScrollReveal>\n1064\t\n1065\t      <ScrollReveal>\n1066\t        <div className=\"mt-16 rounded-[2rem] bg-white p-5 ring-1 ring-foreground/[0.06] lg:mt-20 lg:p-8\">\n1067\t          <div className=\"mb-6 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n1068\t            <div>\n1069\t              <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n1070\t                Décider selon l'état réel\n1071\t              </p>\n1072\t              <h3 className=\"mt-4 max-w-xl font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n1073\t                Quelle solution pour votre toiture ?\n1074\t              </h3>\n1075\t            </div>\n1076\t            <PrimaryCTA label={page.roi.cta} href=\"/diagnostic\" />\n1077\t          </div>\n1078\t          <SituationCards rows={page.roi.situation.rows} />\n1079\t        </div>\n1080\t      </ScrollReveal>\n1081\t    </div>\n1082\t  </section>\n1083\t  );\n1084\t};\n1085\t\n1086\tconst DiagnosticCTA = ({ title, text }: { title: string; text: string }) => (\n1087\t  <section className=\"relative overflow-hidden bg-[#182A3A] py-14 text-white lg:py-20\">\n1088\t    <div\n1089\t      aria-hidden=\"true\"\n1090\t      className=\"absolute inset-0 opacity-35\"\n1091\t      style={{\n1092\t        backgroundImage:\n1093\t          'radial-gradient(circle at 16% 20%, rgba(83, 207, 166, 0.22), transparent 28%), linear-gradient(115deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 120px)',\n1094\t      }}\n1095\t    />\n1096\t    <div className=\"container relative mx-auto px-4 lg:px-8\">\n1097\t      <ScrollReveal>\n1098\t        <div className=\"mx-auto grid max-w-5xl gap-7 lg:grid-cols-[1fr_auto] lg:items-center\">\n1099\t          <div>\n1100\t            <h2 className=\"font-satoshi text-2xl font-black leading-tight lg:text-4xl\">{title}</h2>\n1101\t            <p className=\"mt-3 max-w-2xl font-body text-sm leading-relaxed text-white/68 lg:text-base\">{text}</p>\n1102\t          </div>\n1103\t          <div className=\"flex flex-col gap-3 sm:flex-row lg:justify-end\">\n1104\t            <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n1105\t          </div>\n1106\t        </div>\n1107\t      </ScrollReveal>\n1108\t    </div>\n1109\t  </section>\n1110\t);\n1111\t\n1112\tconst MethodSection = ({ page }: { page: RoofPageData }) => {\n1113\t  const [videoOpen, setVideoOpen] = useState(false);\n1114\t\n1115\t  return (\n1116\t    <section className=\"bg-white py-16 lg:py-32\">\n1117\t      <div className=\"container mx-auto px-4 lg:px-8\">\n1118\t        <div className=\"flex flex-col items-start gap-12 lg:flex-row lg:gap-20\">\n1119\t          <div className=\"lg:sticky lg:top-[120px] lg:w-[40%]\">\n1120\t            <ScrollReveal>\n1121\t              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/50\">\n1122\t                Le process\n1123\t              </p>\n1124\t              <h2\n1125\t                className=\"mb-6 font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n1126\t                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n1127\t              >\n1128\t                4 étapes. Zéro interruption.\n1129\t                <span className=\"text-foreground/30\"> Zéro risque.</span>\n1130\t              </h2>\n1131\t              <p className=\"mb-8 font-body text-base leading-relaxed text-muted-foreground lg:text-lg\">\n1132\t                {page.method.reassurance}\n1133\t              </p>\n1134\t              <button\n1135\t                onClick={() => setVideoOpen(true)}\n1136\t                className=\"group flex items-center gap-3 text-primary transition-colors hover:text-primary\"\n1137\t              >\n1138\t                <span className=\"flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 transition-colors group-hover:bg-primary/20\">\n1139\t                  <Play className=\"h-5 w-5\" />\n1140\t                </span>\n1141\t                <span className=\"font-body text-sm font-semibold\">Voir un chantier en 2 minutes</span>\n1142\t              </button>\n1143\t            </ScrollReveal>\n1144\t          </div>\n1145\t\n1146\t          <div className=\"relative lg:w-[60%]\">\n1147\t            <div className=\"absolute bottom-4 left-5 top-4 w-px bg-gradient-to-b from-primary/25 via-primary/10 to-transparent lg:left-6\" />\n1148\t            <div className=\"flex flex-col\">\n1149\t              {page.method.steps.map((step, index) => {\n1150\t                const Icon = methodIcons[index] ?? CheckCircle2;\n1151\t                return (\n1152\t                  <ScrollReveal key={step.title}>\n1153\t                    <div className=\"group relative flex items-start gap-5 py-8 lg:gap-7 lg:py-10\">\n1154\t                      <span className=\"relative z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-primary/20 bg-primary/10 transition-colors duration-500 group-hover:bg-primary/15 lg:h-12 lg:w-12\">\n1155\t                        <Icon className=\"h-4 w-4 text-primary lg:h-5 lg:w-5\" />\n1156\t                      </span>\n1157\t                      <div className=\"pt-1\">\n1158\t                        <span className=\"mb-2 block font-body text-xs font-bold uppercase tracking-widest text-primary/50\">\n1159\t                          Étape 0{index + 1}\n1160\t                        </span>\n1161\t                        <h3 className=\"mb-2 font-satoshi text-xl font-bold text-foreground lg:text-2xl\">\n1162\t                          {step.title}\n1163\t                        </h3>\n1164\t                        <p className=\"max-w-md font-body text-sm leading-relaxed text-muted-foreground lg:text-base\">\n1165\t                          {step.text}\n1166\t                        </p>\n1167\t                      </div>\n1168\t                    </div>\n1169\t                    {index < page.method.steps.length - 1 && <div className=\"ml-5 h-px bg-border lg:ml-6\" />}\n1170\t                  </ScrollReveal>\n1171\t                );\n1172\t              })}\n1173\t            </div>\n1174\t\n1175\t            <ScrollReveal>\n1176\t              <div className=\"ml-16 mt-4 lg:ml-20\">\n1177\t                <p className=\"font-body text-xs font-medium text-muted-foreground/50\">\n1178\t                  Durée moyenne : 3 jours · Sans arrêt d'activité\n1179\t                </p>\n1180\t              </div>\n1181\t            </ScrollReveal>\n1182\t          </div>\n1183\t        </div>\n1184\t      </div>\n1185\t\n1186\t      <Dialog open={videoOpen} onOpenChange={setVideoOpen}>\n1187\t        <DialogContent className=\"max-w-4xl overflow-hidden border-none bg-black p-0\">\n1188\t          <div className=\"aspect-video\">\n1189\t            <iframe\n1190\t              src=\"https://www.youtube.com/embed/9HOB5OVH0Ac?autoplay=1\"\n1191\t              className=\"h-full w-full\"\n1192\t              allow=\"autoplay; encrypted-media\"\n1193\t              allowFullScreen\n1194\t              title=\"Covalba Cool Roof, un chantier en 2 minutes\"\n1195\t            />\n1196\t          </div>\n1197\t        </DialogContent>\n1198\t      </Dialog>\n1199\t    </section>\n1200\t  );\n1201\t};\n1202\t\n1203\tconst ProofSection = ({ page }: { page: RoofPageData }) => (\n1204\t  <section className=\"bg-[#192A3A] py-20 text-white lg:py-32\">\n1205\t    <div className=\"container mx-auto px-4 lg:px-8\">\n1206\t      <ScrollReveal>\n1207\t        <div className=\"mb-12 grid gap-8 lg:grid-cols-[0.9fr_1.1fr] lg:items-end\">\n1208\t          <div>\n1209\t            <Label light>{page.proof.badge}</Label>\n1210\t            <h2 className=\"font-satoshi text-3xl font-black leading-tight sm:text-4xl lg:text-5xl\">{page.proof.title}</h2>\n1211\t          </div>\n1212\t          <p className=\"max-w-xl font-body text-base leading-relaxed text-white/62\">\n1213\t            Des chiffres de terrain à confirmer chantier par chantier, utilisés ici comme repères de performance.\n1214\t          </p>\n1215\t        </div>\n1216\t      </ScrollReveal>\n1217\t\n1218\t      <ScrollReveal stagger>\n1219\t        <div className=\"grid gap-4 md:grid-cols-3\">\n1220\t          {page.proof.figures.map((figure, index) => {\n1221\t            const Icon = proofIcons[index] ?? BadgeCheck;\n1222\t            return (\n1223\t              <article key={figure.value} className=\"rounded-2xl border border-white/10 bg-white/[0.045] p-7\">\n1224\t                <Icon className=\"mb-6 h-6 w-6 text-teal-vivid\" strokeWidth={1.7} />\n1225\t                <p className=\"font-satoshi text-3xl font-black leading-none text-white lg:text-4xl\">{figure.value}</p>\n1226\t                <p className=\"mt-3 font-body text-sm leading-relaxed text-white/60\">{figure.label}</p>\n1227\t              </article>\n1228\t            );\n1229\t          })}\n1230\t        </div>\n1231\t      </ScrollReveal>\n1232\t\n1233\t      <ScrollReveal>\n1234\t        <div className=\"mt-14 border-t border-white/10 pt-10\">\n1235\t          <p className=\"mb-8 text-center font-body text-[10px] font-bold uppercase tracking-[0.22em] text-white/42\">\n1236\t            Références clients\n1237\t          </p>\n1238\t          <div className=\"flex flex-wrap items-center justify-center gap-x-10 gap-y-7 lg:gap-x-14\">\n1239\t            {logos.map((logo) => (\n1240\t              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n1241\t                <img\n1242\t                  src={logo.url}\n1243\t                  alt={logo.name}\n1244\t                  className=\"max-h-7 max-w-full object-contain opacity-45 transition-opacity duration-300 hover:opacity-80\"\n1245\t                  style={{ filter: 'brightness(0) invert(1)' }}\n1246\t                  loading=\"lazy\"\n1247\t                />\n1248\t              </span>\n1249\t            ))}\n1250\t          </div>\n1251\t        </div>\n1252\t      </ScrollReveal>\n1253\t    </div>\n1254\t  </section>\n1255\t);\n1256\t\n1257\tconst FAQSection = ({ faq }: { faq: RoofPageData['faq'] }) => (\n1258\t  <section className=\"bg-cream py-20 lg:py-32\">\n1259\t    <div className=\"container mx-auto px-4 lg:px-8\">\n1260\t      <div className=\"grid gap-10 lg:grid-cols-[0.85fr_1.15fr] lg:gap-16\">\n1261\t        <ScrollReveal>\n1262\t          <div className=\"lg:sticky lg:top-24\">\n1263\t            <Label>FAQ</Label>\n1264\t            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n1265\t              Vos questions,\n1266\t              <br />\n1267\t              <span className=\"text-foreground/40\">nos réponses.</span>\n1268\t            </h2>\n1269\t            <a href=\"#contact\" className=\"group mt-8 inline-flex items-center gap-2 font-body text-sm font-semibold text-primary transition-all hover:gap-3\">\n1270\t              Une autre question ? Parlons-en\n1271\t              <ArrowRight className=\"h-4 w-4\" />\n1272\t            </a>\n1273\t          </div>\n1274\t        </ScrollReveal>\n1275\t\n1276\t        <ScrollReveal>\n1277\t          <Accordion type=\"single\" collapsible className=\"space-y-3\">\n1278\t            {faq.map((item, index) => (\n1279\t              <AccordionItem\n1280\t                key={item.q}\n1281\t                value={`roof-faq-${index}`}\n1282\t                className=\"rounded-xl border border-foreground/[0.06] bg-white px-5 shadow-[0_12px_34px_rgba(24,42,58,0.04)] data-[state=open]:border-primary/20 lg:px-6\"\n1283\t              >\n1284\t                <AccordionTrigger className=\"py-5 text-left font-satoshi text-base font-bold text-foreground hover:no-underline lg:text-[17px]\">\n1285\t                  {item.q}\n1286\t                </AccordionTrigger>\n1287\t                <AccordionContent className=\"pb-5 font-body text-[14.5px] leading-relaxed text-foreground/67 lg:text-[15px]\">\n1288\t                  {item.a}\n1289\t                </AccordionContent>\n1290\t              </AccordionItem>\n1291\t            ))}\n1292\t          </Accordion>\n1293\t        </ScrollReveal>\n1294\t      </div>\n1295\t    </div>\n1296\t  </section>\n1297\t);\n1298\t\n1299\tconst FinalCTA = ({ page }: { page: RoofPageData }) => (\n1300\t  <section id=\"contact\" className=\"overflow-hidden bg-[#182A3A] py-20 text-white lg:py-32\">\n1301\t    <div className=\"container mx-auto px-4 lg:px-8\">\n1302\t      <ScrollReveal>\n1303\t        <div className=\"mx-auto max-w-4xl text-center\">\n1304\t          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-white sm:text-4xl lg:text-5xl\">\n1305\t            {page.finalCta.title}\n1306\t          </h2>\n1307\t          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-white/72 lg:text-lg\">\n1308\t            {page.finalCta.text}\n1309\t          </p>\n1310\t          <div className=\"mt-9 flex flex-col gap-3 sm:flex-row sm:justify-center\">\n1311\t            <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n1312\t          </div>\n1313\t        </div>\n1314\t      </ScrollReveal>\n1315\t\n1316\t      <ScrollReveal>\n1317\t        <ul className=\"mx-auto mt-12 grid max-w-3xl gap-3 sm:grid-cols-3\">\n1318\t          {page.finalCta.reassurances.map((item) => (\n1319\t            <li key={item} className=\"flex items-center justify-center gap-2.5 rounded-lg border border-white/[0.12] bg-white/[0.045] px-4 py-3\">\n1320\t              <CalendarCheck className=\"h-4 w-4 shrink-0 text-teal-vivid\" strokeWidth={1.8} />\n1321\t              <span className=\"font-body text-sm text-white/[0.76]\">{item}</span>\n1322\t            </li>\n1323\t          ))}\n1324\t        </ul>\n1325\t      </ScrollReveal>\n1326\t    </div>\n1327\t  </section>\n1328\t);\n1329\t\n1330\tconst SEOContent = ({ page }: { page: RoofPageData }) => (\n1331\t  <section className=\"bg-white py-20 lg:py-28\">\n1332\t    <div className=\"container mx-auto px-4 lg:px-8\">\n1333\t      <ScrollReveal>\n1334\t        <article className=\"mx-auto max-w-3xl\">\n1335\t          <p className=\"mb-5 font-body text-[10px] font-bold uppercase tracking-[0.25em] text-primary/55\">\n1336\t            Guide support\n1337\t          </p>\n1338\t          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground lg:text-4xl\">\n1339\t            Tout comprendre sur le {page.eyebrow.toLowerCase()}.\n1340\t          </h2>\n1341\t          <p className=\"mt-6 font-body text-base leading-relaxed text-foreground/68 lg:text-lg\">\n1342\t            {page.seo.intro}\n1343\t          </p>\n1344\t\n1345\t          <div className=\"mt-10 space-y-9\">\n1346\t            {page.seo.sections.map((section) => (\n1347\t              <section key={section.title}>\n1348\t                <h3 className=\"font-satoshi text-2xl font-bold leading-tight text-foreground\">{section.title}</h3>\n1349\t                <p className=\"mt-3 font-body text-base leading-relaxed text-foreground/66\">{section.text}</p>\n1350\t              </section>\n1351\t            ))}\n1352\t          </div>\n1353\t\n1354\t          <div className=\"mt-12 rounded-2xl bg-cream p-6 ring-1 ring-foreground/[0.05]\">\n1355\t            <h3 className=\"font-satoshi text-lg font-bold text-foreground\">Sources</h3>\n1356\t            <ul className=\"mt-4 space-y-2\">\n1357\t              {page.seo.sources.map((source) => (\n1358\t                <li key={source.href}>\n1359\t                  <a href={source.href} className=\"font-body text-sm leading-relaxed text-primary underline-offset-4 hover:underline\" target=\"_blank\" rel=\"noreferrer\">\n1360\t                    {source.label}\n1361\t                  </a>\n1362\t                </li>\n1363\t              ))}\n1364\t            </ul>\n1365\t          </div>\n1366\t        </article>\n1367\t      </ScrollReveal>\n1368\t    </div>\n1369\t  </section>\n1370\t);\n1371\t\n1372\tconst buildFaqSchema = (page: RoofPageData) => ({\n1373\t  '@context': 'https://schema.org',\n1374\t  '@type': 'FAQPage',\n1375\t  mainEntity: page.faq.map((item) => ({\n1376\t    '@type': 'Question',\n1377\t    name: item.q,\n1378\t    acceptedAnswer: {\n1379\t      '@type': 'Answer',\n1380\t      text: item.a,\n1381\t    },\n1382\t  })),\n1383\t});\n1384\t\n1385\tconst buildBreadcrumbSchema = (page: RoofPageData) => ({\n1386\t  '@context': 'https://schema.org',\n1387\t  '@type': 'BreadcrumbList',\n1388\t  itemListElement: [\n1389\t    { '@type': 'ListItem', position: 1, name: 'Accueil', item: SITE_URL },\n1390\t    { '@type': 'ListItem', position: 2, name: 'Toitures', item: `${SITE_URL}/toitures` },\n1391\t    { '@type': 'ListItem', position: 3, name: page.breadcrumb, item: page.canonical },\n1392\t  ],\n1393\t});\n1394\t\n1395\tconst RoofPageTemplate = ({ page }: RoofPageTemplateProps) => {\n1396\t  usePageMeta({\n1397\t    title: page.title,\n1398\t    description: page.description,\n1399\t    canonical: page.canonical,\n1400\t  });\n1401\t  useJsonLd(`roof-${page.slug}-faq`, buildFaqSchema(page));\n1402\t  useJsonLd(`roof-${page.slug}-breadcrumb`, buildBreadcrumbSchema(page));\n1403\t\n1404\t  return (\n1405\t    <>\n1406\t      <Navbar />\n1407\t      <main>\n1408\t        <RoofHero page={page} />\n1409\t        <ProblemSection page={page} />\n1410\t        <SolutionSection page={page} />\n1411\t        <BenefitsSection page={page} />\n1412\t        <ROISection page={page} />\n1413\t        <DiagnosticCTA title={page.midCta.title} text={page.midCta.text} />\n1414\t        <MethodSection page={page} />\n1415\t        <ProofSection page={page} />\n1416\t        <FAQSection faq={page.faq} />\n1417\t        <FinalCTA page={page} />\n1418\t        <SEOContent page={page} />\n1419\t      </main>\n1420\t      <Footer />\n1421\t      <StickyMobileCTA />\n1422\t    </>\n1423\t  );\n1424\t};\n1425\t\n1426\texport default RoofPageTemplate;\n1427\t"}]},"uuid":"ecc7cdb0-ff4d-4929-95c8-8117a4255a95","timestamp":"2026-06-22T00:03:23.692Z","toolUseResult":{"type":"text","file":{"filePath":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/roof/RoofPageTemplate.tsx","content":"import { useState } from 'react';\nimport {\n  ArrowRight,\n  BadgeCheck,\n  CalendarClock,\n  CalendarCheck,\n  Check,\n  CheckCircle2,\n  ChevronDown,\n  ClipboardCheck,\n  Factory,\n  HelpCircle,\n  Play,\n  ShieldCheck,\n  ShieldAlert,\n  Sparkles,\n  SunMedium,\n  Thermometer,\n  TrendingDown,\n  Users,\n  Wrench,\n  type LucideIcon,\n} from 'lucide-react';\n\nimport Footer from '@/components/Footer';\nimport Navbar from '@/components/Navbar';\nimport ScrollReveal from '@/components/ScrollReveal';\nimport StickyMobileCTA from '@/components/StickyMobileCTA';\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';\nimport { Dialog, DialogContent } from '@/components/ui/dialog';\nimport { useJsonLd, usePageMeta } from '@/hooks/usePageMeta';\nimport type { RoofPageData } from '@/data/roofPages';\nimport coolRoofLayers from '@/assets/cool-roof-layers.webp';\nimport covaMetalProduct from '@/assets/produits/Covalba-CovaMetal20-Duo-Produit.webp';\nimport covaSealProduct from '@/assets/produits/Covalba-CovaSeal20-Duo-Produit.webp';\nimport covaTherm8Product from '@/assets/produits/Covalba-CovaTherm8-Produit.webp';\nimport covaTherm20Product from '@/assets/produits/Covalba-CovaTherm20-Produit.webp';\n\ninterface RoofPageTemplateProps {\n  page: RoofPageData;\n}\n\nconst SITE_URL = 'https://www.covalba.fr';\n\nconst cardIcons: LucideIcon[] = [SunMedium, Wrench, Factory, ShieldCheck];\nconst methodIcons: LucideIcon[] = [ClipboardCheck, CalendarClock, ShieldCheck, TrendingDown];\nconst proofIcons: LucideIcon[] = [Thermometer, ShieldCheck, BadgeCheck];\nconst problemIcons: LucideIcon[] = [Users, SunMedium, ShieldAlert];\n\nconst logos = [\n  { name: 'Continental', url: '/logos/logo-continental.svg' },\n  { name: 'Thales', url: '/logos/logo-thales.svg' },\n  { name: 'Nestlé', url: '/logos/logo-nestle.svg' },\n  { name: 'SNCF', url: '/logos/logo-sncf.svg' },\n  { name: 'Leclerc', url: '/logos/logo-leclerc.svg' },\n  { name: 'Super U', url: '/logos/logo-super-u.svg' },\n];\n\nconst displayCell = (value: string) => value.replace(/EUR/g, '€').replace(/m2/g, 'm²');\n\nconst formatCurrency = (value: number) =>\n  `${new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 0 }).format(Math.round(value))} €`;\n\nconst parsePricePerM2 = (value: string) => {\n  const match = displayCell(value).match(/\\(([\\d,.]+)\\s*€\\/m²\\)/);\n  if (!match) return null;\n\n  return Number.parseFloat(match[1].replace(',', '.'));\n};\n\nconst getTreatmentRange = (page: RoofPageData, situation: string) => {\n  const text = `${page.slug} ${situation}`.toLowerCase();\n\n  if (text.includes('covaseal') || text.includes('vieillissante') || text.includes('fuite')) {\n    return { min: 28, max: 38, label: 'CovaSeal 20 estimatif' };\n  }\n\n  if (text.includes('covametal') || text.includes('corrosion') || text.includes('tôle')) {\n    return { min: 24, max: 32, label: 'CovaMetal 20 estimatif' };\n  }\n\n  if (text.includes('fibrociment') || text.includes('plaques')) {\n    return { min: 22, max: 28, label: 'CovaTherm support sensible' };\n  }\n\n  return { min: 20, max: 25, label: 'CovaTherm estimatif' };\n};\n\nconst getFallbackPricePerM2 = (option: string, optionIndex: number, treatmentMidpoint: number) => {\n  const lower = option.toLowerCase();\n\n  if (optionIndex === 0) return treatmentMidpoint;\n  if (lower.includes('anticorrosion simple')) return 16;\n  if (lower.includes('remplacement')) return 120;\n  if (lower.includes('membrane neuve')) return 90;\n  if (lower.includes('isolation')) return 150;\n\n  return null;\n};\n\nconst benefitImages = [\n  '/images/toitures/benefit-confort.webp',\n  '/images/toitures/benefit-protection-toiture.webp',\n  '/images/toitures/benefit-clim.webp',\n  '/images/toitures/benefit-chantier-evite.webp',\n];\n\nconst feedbackRoofBenefitImages = [\n  '/images/toitures/feedback-batiment-tempere.png',\n  '/images/toitures/feedback-etancheite.png',\n  '/images/toitures/feedback-economie-clim-1.png',\n  '/images/toitures/feedback-economie-clim-3.png',\n];\n\nconst getBenefitImage = (page: RoofPageData, benefit: TextCard, index: number) => {\n  if (['membrane-bitumineuse', 'toiture-plate'].includes(page.slug)) {\n    return feedbackRoofBenefitImages[index] ?? feedbackRoofBenefitImages[0];\n  }\n\n  const text = `${benefit.title} ${benefit.text}`.toLowerCase();\n\n  if (page.slug === 'bac-acier' && (text.includes('tôle') || text.includes('corrosion'))) {\n    return '/images/toitures/support-bac-acier-cool.webp';\n  }\n\n  if (text.includes('facture') || text.includes('clim') || text.includes('froid')) {\n    return '/images/toitures/benefit-clim.webp';\n  }\n\n  if (text.includes('chantier') || text.includes('réfection') || text.includes('remplacement') || text.includes('dépose')) {\n    return '/images/toitures/benefit-chantier-evite.webp';\n  }\n\n  if (text.includes('protég') || text.includes('étanchéité') || text.includes('corrosion') || text.includes('durée')) {\n    return '/images/toitures/benefit-protection-toiture.webp';\n  }\n\n  return benefitImages[index] ?? benefitImages[0];\n};\n\nconst supportVisuals: Record<string, { hot: string; cool: string; hotLabel: string; coolLabel: string }> = {\n  'membrane-bitumineuse': {\n    hot: '/images/toitures/support-bitume-chaud.webp',\n    cool: '/images/toitures/feedback-economie-clim-2.png',\n    hotLabel: 'Sans cool roof : membrane bitume en absorption',\n    coolLabel: 'Avec cool roof : membrane réfléchissante',\n  },\n  'bac-acier': {\n    hot: '/images/toitures/support-bac-acier-chaud.webp',\n    cool: '/images/toitures/support-bac-acier-cool.webp',\n    hotLabel: 'Sans cool roof : métal en surchauffe',\n    coolLabel: 'Avec cool roof : bac acier réfléchi et protégé',\n  },\n  'toiture-plate': {\n    hot: '/images/toitures/support-toiture-plate-chaud.webp',\n    cool: '/images/toitures/feedback-economie-clim-2.png',\n    hotLabel: 'Sans cool roof : terrasse en absorption',\n    coolLabel: 'Avec cool roof : terrasse réfléchissante',\n  },\n  fibrociment: {\n    hot: '/images/toitures/support-fibrociment-chaud.webp',\n    cool: '/images/toitures/support-fibrociment-cool.webp',\n    hotLabel: 'Sans cool roof : fibrociment en absorption',\n    coolLabel: 'Avec cool roof : plaques protégées',\n  },\n  'tuiles-ciment': {\n    hot: '/images/toitures/support-tuiles-ciment-chaud.webp',\n    cool: '/images/toitures/support-tuiles-ciment-cool.webp',\n    hotLabel: 'Sans cool roof : tuiles qui stockent la chaleur',\n    coolLabel: 'Avec cool roof : tuiles réfléchissantes',\n  },\n};\n\nconst layerTitles = [\"Primaire d'accroche\", 'Résine réfléchissante', 'Vernis de protection'];\n\nconst layerColors = [\n  {\n    pill: 'bg-terracotta text-white',\n    dot: 'bg-terracotta',\n    chevron: 'text-terracotta',\n  },\n  {\n    pill: 'bg-foreground text-white',\n    dot: 'bg-foreground',\n    chevron: 'text-foreground',\n  },\n  {\n    pill: 'bg-primary text-white',\n    dot: 'bg-primary',\n    chevron: 'text-primary',\n  },\n];\n\nconst layerPositions = [\n  { top: '56.7%', left: '36.5%' },\n  { top: '48.2%', left: '36.5%' },\n  { top: '36.3%', left: '35.6%' },\n];\n\nconst getProblemTitle = (title: string) => {\n  const map: Record<string, { strong: string; muted: string }> = {\n    \"Une toiture bitume noire, c'est un radiateur posé sur votre bâtiment.\": {\n      strong: 'Votre toiture chauffe.',\n      muted: 'Votre bâtiment encaisse.',\n    },\n    'Le bac acier chauffe vite, rouille vite, et fatigue vite.': {\n      strong: 'Le métal chauffe vite.',\n      muted: 'La toiture fatigue aussi.',\n    },\n    'À plat, votre toiture prend le soleil toute la journée.': {\n      strong: 'Votre terrasse prend tout.',\n      muted: \"Chaleur, UV, étanchéité.\",\n    },\n    'Le fibrociment chauffe, vieillit, et coûte cher à déposer.': {\n      strong: 'Le fibrociment chauffe.',\n      muted: 'Le déposer coûte cher.',\n    },\n    'Des tuiles ciment foncées, des combles en surchauffe.': {\n      strong: 'Les tuiles stockent la chaleur.',\n      muted: 'Les combles la subissent.',\n    },\n  };\n\n  return map[title] ?? { strong: title, muted: '' };\n};\n\nconst getLayerDescription = (index: number) => {\n  if (index === 0) {\n    return \"S'adapte au support existant. Chaque toiture a sa chimie, chaque chimie demande une préparation et une accroche adaptées.\";\n  }\n\n  if (index === 1) {\n    return \"C'est le coeur du système. La surface réfléchit le rayonnement solaire au lieu de l'absorber, donc la chaleur rentre moins dans le bâtiment.\";\n  }\n\n  return \"Un top coat protège la résine des UV, de la pollution et du vieillissement prématuré pour conserver la performance dans le temps.\";\n};\n\nconst getLayerExpert = (index: number) => {\n  const notes = [\n    \"Un primaire inadapté, c'est un système qui cloque et se décolle. Le support décide de tout.\",\n    \"Le cool roof traite la cause : la toiture chauffe moins, donc le bâtiment reçoit moins de calories.\",\n    \"La différence entre une peinture et un investissement, c'est la tenue dans le temps. La finition est là pour protéger la performance.\",\n  ];\n\n  return notes[index] ?? notes[0];\n};\n\nconst Label = ({ children, light = false }: { children: string; light?: boolean }) => (\n  <div className=\"mb-5 flex items-center gap-3\">\n    <span className={`h-px w-9 ${light ? 'bg-teal-vivid/70' : 'bg-primary/65'}`} aria-hidden=\"true\" />\n    <span\n      className={`font-body text-[11px] font-bold uppercase tracking-[0.22em] ${\n        light ? 'text-teal-vivid' : 'text-primary'\n      }`}\n    >\n      {children}\n    </span>\n  </div>\n);\n\nconst PrimaryCTA = ({ label, href }: { label: string; href: string }) => (\n  <a\n    href={href}\n    className=\"group inline-flex items-center justify-center gap-2 rounded-full cta-gradient py-2.5 pl-6 pr-2 font-semibold text-white\"\n  >\n    <span className=\"text-sm\">{label}</span>\n    <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n      <ArrowRight className=\"h-3.5 w-3.5\" />\n    </span>\n  </a>\n);\n\nconst RoofHero = ({ page }: { page: RoofPageData }) => {\n  const [form, setForm] = useState({ climate: '', surface: '', email: '' });\n  const [submitted, setSubmitted] = useState(false);\n  const surfaceNum = parseFloat(form.surface) || 0;\n  const estimate = surfaceNum ? `${new Intl.NumberFormat('fr-FR').format(surfaceNum * 22.5)} €` : '20-25 €/m²';\n\n  return (\n    <section\n      aria-labelledby={`${page.slug}-hero-title`}\n      className=\"relative isolate flex min-h-[100svh] flex-col overflow-hidden bg-navy text-cream\"\n    >\n      <img\n        src={page.heroImage}\n        alt={page.heroImageAlt}\n        className=\"absolute inset-0 -z-10 size-full scale-[1.02] object-cover\"\n        style={page.slug === 'membrane-bitumineuse' ? { objectPosition: 'center 28%' } : undefined}\n        loading=\"eager\"\n        decoding=\"async\"\n      />\n      <div\n        aria-hidden=\"true\"\n        className=\"absolute inset-0 -z-10 hidden bg-gradient-to-r from-navy/[0.94] via-navy/62 to-navy/25 lg:block\"\n      />\n      <div\n        aria-hidden=\"true\"\n        className={`absolute inset-0 -z-10 bg-gradient-to-b lg:hidden ${\n          page.slug === 'membrane-bitumineuse'\n            ? 'from-navy/92 via-navy/58 to-navy/96'\n            : 'from-navy/84 via-navy/38 to-navy/96'\n        }`}\n      />\n      <div aria-hidden=\"true\" className=\"absolute inset-x-0 bottom-0 -z-10 h-[28%] bg-gradient-to-t from-navy via-navy/85 to-transparent\" />\n\n      <div className=\"container relative z-10 mx-auto flex flex-1 items-center px-6 pb-10 pt-24 lg:px-12 lg:pb-16 lg:pt-28\">\n        <div className=\"grid w-full grid-cols-1 items-center gap-10 lg:grid-cols-[55fr_45fr] lg:gap-12\">\n          <div className=\"max-w-2xl space-y-6 lg:space-y-8\">\n            <ScrollReveal>\n              <nav className=\"flex items-center gap-2 font-body text-xs text-cream/50\">\n                <a href=\"/\" className=\"transition-colors hover:text-cream/75\">Accueil</a>\n                <span className=\"text-cream/28\">/</span>\n                <span>Toitures</span>\n                <span className=\"text-cream/28\">/</span>\n                <span className=\"text-cream/78\">{page.breadcrumb}</span>\n              </nav>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <h1\n                id={`${page.slug}-hero-title`}\n                className=\"font-satoshi font-bold leading-[1.02] text-white\"\n                style={{ fontSize: 'clamp(2.25rem, 4.6vw, 4rem)', letterSpacing: '-0.03em' }}\n              >\n                {page.h1}\n              </h1>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <p\n                className=\"max-w-xl font-satoshi font-medium leading-[1.3] text-cream/66\"\n                style={{ fontSize: 'clamp(1rem, 1.5vw, 1.375rem)', letterSpacing: '-0.005em' }}\n              >\n                {page.lead}\n              </p>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <ul className=\"hidden gap-4 pt-1 sm:flex sm:flex-wrap lg:gap-6\" aria-label=\"Bénéfices clés\">\n                {page.heroStats.map((stat, index) => {\n                  const Icon = [Thermometer, ShieldCheck, TrendingDown][index] ?? BadgeCheck;\n                  return (\n                    <li\n                      key={stat.label}\n                      className={`flex items-start gap-3 ${index !== 0 ? 'sm:border-l sm:border-white/10 sm:pl-6' : ''}`}\n                    >\n                      <Icon className=\"mt-0.5 h-[18px] w-[18px] shrink-0 text-teal-vivid\" strokeWidth={1.5} />\n                      <div className=\"leading-tight\">\n                        <p className=\"font-satoshi text-[15px] font-bold text-white lg:text-base\">{stat.value}</p>\n                        <p className=\"mt-1 font-body text-[12.5px] text-cream/55 lg:text-[13px]\">{stat.label}</p>\n                      </div>\n                    </li>\n                  );\n                })}\n              </ul>\n            </ScrollReveal>\n          </div>\n\n          <ScrollReveal>\n            <div className=\"rounded-[1.5rem] border border-white/[0.08] bg-white/[0.04] p-1.5 backdrop-blur-sm\">\n              <div className=\"rounded-[calc(1.5rem-6px)] border border-white/[0.06] bg-[#0b1a2b]/80 p-4 shadow-[inset_0_1px_1px_rgba(255,255,255,0.06)] backdrop-blur-xl lg:p-7\">\n                {!submitted ? (\n                  <>\n                    <h2 className=\"mb-1 text-center font-satoshi text-xl font-bold text-white lg:text-2xl\">\n                      Estimez le gain possible\n                    </h2>\n                    <p className=\"mb-4 text-center font-body text-[12px] text-white/50\">\n                      Réponse personnalisée sous 24h\n                    </p>\n                    <form\n                      onSubmit={(event) => {\n                        event.preventDefault();\n                        setSubmitted(true);\n                      }}\n                      className=\"space-y-2.5 lg:space-y-3\"\n                    >\n                      <input\n                        value={page.breadcrumb}\n                        readOnly\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white outline-none\"\n                      />\n                      <select\n                        required\n                        value={form.climate}\n                        onChange={(e) => setForm({ ...form, climate: e.target.value })}\n                        className=\"w-full appearance-none rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      >\n                        <option value=\"\" disabled className=\"bg-card text-foreground\">Bâtiment climatisé ?</option>\n                        {['Oui', 'Non', 'Partiellement', 'À confirmer'].map((option) => (\n                          <option key={option} value={option} className=\"bg-card text-foreground\">{option}</option>\n                        ))}\n                      </select>\n                      <input\n                        type=\"number\"\n                        placeholder=\"Surface toiture (m²)\"\n                        required\n                        min=\"1\"\n                        value={form.surface}\n                        onChange={(e) => setForm({ ...form, surface: e.target.value })}\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      />\n                      <input\n                        type=\"email\"\n                        placeholder=\"Email professionnel\"\n                        required\n                        value={form.email}\n                        onChange={(e) => setForm({ ...form, email: e.target.value })}\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      />\n\n                      <button\n                        type=\"submit\"\n                        className=\"group mt-1 flex w-full items-center justify-center gap-3 rounded-full cta-gradient py-3 font-semibold text-white active:scale-[0.98]\"\n                      >\n                        <span className=\"text-[15px]\">Voir mon potentiel</span>\n                        <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n                          <ArrowRight className=\"h-4 w-4\" />\n                        </span>\n                      </button>\n                    </form>\n\n                    <p className=\"mt-3 text-center font-body text-[11px] text-white/35\">\n                      Sans engagement · Étude adaptée au support\n                    </p>\n                  </>\n                ) : (\n                  <div className=\"py-4 text-center\">\n                    <div className=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary/20\">\n                      <ShieldCheck className=\"h-6 w-6 text-primary\" />\n                    </div>\n                    <p className=\"mb-1 font-body text-sm text-white/60\">Base indicative pour {new Intl.NumberFormat('fr-FR').format(surfaceNum)} m²</p>\n                    <p className=\"mb-4 font-satoshi text-2xl font-bold text-white\">{estimate}</p>\n                    <p className=\"mb-4 font-body text-[13px] text-white/50\">\n                      Un conseiller affine le chiffrage selon l'état réel de la toiture.\n                    </p>\n                    <button onClick={() => setSubmitted(false)} className=\"font-body text-sm font-semibold text-primary hover:underline\">\n                      Modifier ma demande\n                    </button>\n                  </div>\n                )}\n              </div>\n            </div>\n          </ScrollReveal>\n        </div>\n      </div>\n\n      <div className=\"relative z-10 hidden border-t border-white/10 bg-navy/78 backdrop-blur-md lg:block\">\n        <div className=\"container mx-auto flex items-center gap-8 px-12 py-5\">\n          <p className=\"shrink-0 font-body text-[10px] font-semibold uppercase tracking-[0.24em] text-cream/45\">\n            Ils nous font confiance\n          </p>\n          <div className=\"flex flex-1 items-center justify-between gap-8\">\n            {logos.map((logo) => (\n              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n                <img\n                  src={logo.url}\n                  alt={logo.name}\n                  className=\"max-h-7 max-w-full object-contain opacity-65 brightness-0 invert transition-opacity duration-300 hover:opacity-95\"\n                  loading=\"eager\"\n                  decoding=\"async\"\n                />\n              </span>\n            ))}\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst ProblemSection = ({ page }: { page: RoofPageData }) => {\n  const title = getProblemTitle(page.problem.title);\n  const visual = supportVisuals[page.slug] ?? supportVisuals['membrane-bitumineuse'];\n\n  return (\n    <section className=\"relative overflow-hidden bg-cream py-16 lg:py-36\">\n      <div className=\"container relative mx-auto px-4 lg:px-8\">\n        <ScrollReveal>\n          <div className=\"mx-auto mb-14 max-w-3xl text-center lg:mb-20\">\n            <h2\n              className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n              style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n            >\n              {title.strong}\n              {title.muted && <span className=\"text-foreground/30\"> {title.muted}</span>}\n            </h2>\n          </div>\n        </ScrollReveal>\n\n        <div className=\"grid grid-cols-1 items-center gap-12 lg:grid-cols-12 lg:gap-16\">\n          <div className=\"order-2 lg:order-1 lg:col-span-5\">\n            <ScrollReveal>\n              <div className=\"relative\">\n                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_hsl(var(--terracotta)/0.18)_0%,_transparent_65%)] blur-2xl\" />\n                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_hsl(var(--terracotta-dark)/0.25),_0_10px_30px_-5px_hsl(var(--terracotta)/0.12)]\">\n                  <img\n                    src={visual.hot}\n                    alt=\"\"\n                    className=\"h-full w-full object-contain p-4\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                </div>\n              </div>\n              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-terracotta/60\">\n                {visual.hotLabel}\n              </p>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"order-1 lg:order-2 lg:col-span-7 lg:pl-12\">\n            <ScrollReveal stagger>\n              <div\n                className=\"space-y-8 border-l-2 pl-8 lg:space-y-10 lg:pl-10\"\n                style={{ borderImage: 'linear-gradient(to bottom, hsl(var(--terracotta)), hsl(var(--terracotta-dark))) 1' }}\n              >\n                {page.problem.cards.map((card, index) => {\n                  const Icon = problemIcons[index] ?? HelpCircle;\n                  return (\n                    <div key={card.title}>\n                      <div className=\"mb-2 flex items-center gap-3\">\n                        <Icon className=\"h-5 w-5 text-terracotta\" strokeWidth={1.8} />\n                        <h3\n                          className=\"font-satoshi text-xl font-bold text-foreground lg:text-2xl\"\n                          style={{ letterSpacing: '-0.02em', lineHeight: 1.2 }}\n                        >\n                          {card.title}\n                        </h3>\n                      </div>\n                      <p className=\"font-body text-base leading-relaxed text-foreground/55\">{card.text}</p>\n                    </div>\n                  );\n                })}\n              </div>\n            </ScrollReveal>\n          </div>\n        </div>\n\n        <div className=\"mt-20 grid grid-cols-1 items-center gap-12 lg:mt-28 lg:grid-cols-12 lg:gap-16\">\n          <div className=\"order-2 lg:order-2 lg:col-span-5\">\n            <ScrollReveal>\n              <div className=\"relative\">\n                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_rgba(32,150,140,0.18)_0%,_transparent_65%)] blur-2xl\" />\n                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_rgba(20,120,110,0.20),_0_10px_30px_-5px_rgba(32,150,140,0.10)]\">\n                  <img\n                    src={visual.cool}\n                    alt=\"\"\n                    className=\"h-full w-full object-contain p-4\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                </div>\n              </div>\n              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-primary/60\">\n                {visual.coolLabel}\n              </p>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"order-1 lg:order-1 lg:col-span-7 lg:pr-12\">\n            <ScrollReveal>\n              <h3\n                className=\"mb-8 max-w-xl font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                Le cool roof : une réponse concrète\n                <span className=\"text-foreground/30\"> au problème de chaleur.</span>\n              </h3>\n              <p className=\"mb-8 max-w-lg font-body text-base leading-relaxed text-foreground/50\">\n                Une surface claire renvoie le rayonnement solaire avant qu'il ne pénètre dans le bâtiment. La toiture chauffe moins, les derniers niveaux respirent, et le support subit moins d'UV et de chocs thermiques.\n              </p>\n              <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n            </ScrollReveal>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst SolutionSection = ({ page }: { page: RoofPageData }) => {\n  const [active, setActive] = useState(0);\n\n  return (\n    <section className=\"relative bg-white\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n        <ScrollReveal>\n          <div className=\"flex flex-col gap-8 pb-10 pt-16 lg:flex-row lg:items-end lg:gap-16 lg:pb-16 lg:pt-32\">\n            <div className=\"lg:w-2/3\">\n              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-muted-foreground\">\n                {page.solution.badge}\n              </p>\n              <h2\n                className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                <span className=\"text-foreground/30\">Un système conçu pour </span>\n                réfléchir, protéger et durer.\n              </h2>\n            </div>\n            <div className=\"lg:w-1/3 lg:pb-1\">\n              <p className=\"font-body text-base leading-relaxed text-foreground/52 lg:text-right lg:text-lg\">\n                Un primaire, une résine, un vernis qui garantissent la performance de votre toiture dans le temps.\n              </p>\n              <div className=\"mt-5 flex lg:justify-end\">\n                <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n              </div>\n            </div>\n          </div>\n        </ScrollReveal>\n\n        <div className=\"flex flex-col gap-12 pb-24 lg:flex-row lg:gap-16 lg:pb-32\">\n          <div className=\"overflow-hidden rounded-2xl shadow-[0_20px_60px_-15px_rgba(0,0,0,0.08)] lg:hidden\">\n            <img src={coolRoofLayers} alt=\"Coupe du système cool roof Covalba\" className=\"w-full\" loading=\"lazy\" />\n          </div>\n\n          <div className=\"lg:w-1/2 lg:sticky lg:top-[20vh] lg:self-start\">\n            {layerTitles.map((item, index) => {\n              const isOpen = active === index;\n              const colors = layerColors[index] ?? layerColors[0];\n              return (\n                <div key={item} className=\"border-b border-border last:border-b-0\">\n                  <button\n                    onClick={() => setActive(index)}\n                    className=\"group flex w-full items-center gap-4 py-6 text-left\"\n                  >\n                    <span\n                      className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full font-body text-[11px] font-bold transition-all duration-300 ${\n                        isOpen ? colors.pill : 'bg-muted text-muted-foreground'\n                      }`}\n                    >\n                      0{index + 1}\n                    </span>\n                    <h3\n                      className={`flex-1 font-satoshi text-xl font-bold transition-colors duration-300 lg:text-2xl ${\n                        isOpen ? 'text-foreground' : 'text-foreground/50 group-hover:text-foreground/80'\n                      }`}\n                    >\n                      {item}\n                    </h3>\n                    <ChevronDown\n                      className={`h-5 w-5 shrink-0 transition-all duration-300 ${\n                        isOpen ? `${colors.chevron} rotate-180` : 'text-foreground/25'\n                      }`}\n                      strokeWidth={2}\n                    />\n                  </button>\n\n                  <div\n                    className={`grid transition-all duration-500 ${\n                      isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'\n                    }`}\n                  >\n                    <div className=\"overflow-hidden\">\n                      <div className=\"pb-8 pl-6 lg:pl-12\">\n                        <p className=\"mb-6 font-body text-base leading-relaxed text-foreground/65 lg:text-lg\">\n                          {getLayerDescription(index)}\n                        </p>\n                        <div className=\"flex items-start gap-3 rounded-xl bg-foreground/[0.03] p-4\">\n                          <img\n                            src=\"/maxime-bourassin.jpg\"\n                            alt=\"Maxime Bourassin, fondateur Covalba\"\n                            className=\"mt-0.5 h-9 w-9 shrink-0 rounded-full object-cover\"\n                          />\n                          <div>\n                            <p className=\"font-body text-[15px] italic leading-relaxed text-foreground/65 lg:text-base\">\n                              \"{getLayerExpert(index)}\"\n                            </p>\n                            <p className=\"mt-2 font-body text-xs font-semibold text-foreground/50\">\n                              Maxime Bourassin, fondateur\n                            </p>\n                          </div>\n                        </div>\n                        {index === 0 && page.solution.note && (\n                          <p className=\"mt-4 rounded-xl bg-primary/5 p-4 font-body text-sm leading-relaxed text-foreground/62\">\n                            {page.solution.note}\n                          </p>\n                        )}\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              );\n            })}\n          </div>\n\n          <div className=\"hidden lg:block lg:w-1/2\">\n            <div className=\"relative overflow-hidden rounded-2xl shadow-[0_30px_80px_-15px_rgba(0,0,0,0.08)]\">\n              <img src={coolRoofLayers} alt=\"Coupe du système cool roof Covalba\" className=\"w-full\" loading=\"lazy\" />\n              {layerTitles.map((item, index) => {\n                const pos = layerPositions[index];\n                const colors = layerColors[index] ?? layerColors[0];\n                const isActive = active === index;\n                if (!pos) return null;\n\n                return (\n                  <div key={item} className=\"absolute\" style={{ top: pos.top, left: pos.left }}>\n                    <div\n                      className={`absolute -translate-x-1/2 -translate-y-1/2 transition-all duration-500 ${\n                        isActive ? 'scale-125' : 'scale-75 opacity-40'\n                      }`}\n                    >\n                      <div\n                        className={`h-3.5 w-3.5 rounded-full ${\n                          isActive ? colors.dot : 'bg-foreground/20'\n                        } shadow-lg ring-2 ring-white transition-colors duration-500`}\n                      />\n                      {isActive && (\n                        <div className={`absolute inset-0 h-3.5 w-3.5 animate-ping rounded-full ${colors.dot} opacity-30`} />\n                      )}\n                    </div>\n                    <div\n                      className={`pointer-events-none absolute right-5 top-1/2 -translate-y-1/2 transition-all duration-500 ${\n                        isActive ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-0'\n                      }`}\n                    >\n                      <span className={`whitespace-nowrap rounded-full px-4 py-1.5 font-body text-[13px] font-bold shadow-lg ${colors.pill}`}>\n                        {item}\n                      </span>\n                    </div>\n                  </div>\n                );\n              })}\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst BenefitsSection = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-white py-20 lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mb-10 grid gap-8 lg:grid-cols-[0.82fr_1.18fr] lg:items-end lg:gap-14\">\n          <div>\n            <Label>{page.benefits.badge}</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n              {page.benefits.title}\n            </h2>\n          </div>\n          <p className=\"max-w-xl border-l border-primary/30 pl-6 font-body text-base leading-relaxed text-foreground/62 lg:text-lg\">\n            {page.benefits.featured}\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal stagger>\n        <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n          {page.benefits.cards.map((benefit, index) => {\n            const Icon = cardIcons[index] ?? Sparkles;\n            return (\n              <article\n                key={benefit.title}\n                className=\"group overflow-hidden rounded-2xl bg-cream ring-1 ring-foreground/[0.05] transition-all duration-500 hover:-translate-y-1 hover:shadow-[0_24px_70px_-48px_hsl(var(--navy)/0.45)]\"\n              >\n                <div className=\"relative aspect-[4/3] overflow-hidden bg-foreground/[0.04]\">\n                  <img\n                    src={getBenefitImage(page, benefit, index)}\n                    alt=\"\"\n                    className=\"h-full w-full object-cover transition-transform duration-700 group-hover:scale-[1.035]\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                  <div className=\"absolute inset-x-0 bottom-0 h-20 bg-gradient-to-t from-cream to-transparent\" />\n                </div>\n                <div className=\"p-5 lg:p-6\">\n                  <div className=\"mb-4 flex h-10 w-10 items-center justify-center rounded-xl bg-white text-primary shadow-[0_12px_30px_-24px_hsl(var(--navy)/0.35)]\">\n                    <Icon className=\"h-5 w-5\" strokeWidth={1.7} />\n                  </div>\n                  <h3 className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{benefit.title}</h3>\n                  <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/62\">{benefit.text}</p>\n                </div>\n              </article>\n            );\n          })}\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst RoiOptionCards = ({\n  page,\n  surface,\n  treatmentRange,\n}: {\n  page: RoofPageData;\n  surface: number;\n  treatmentRange: ReturnType<typeof getTreatmentRange>;\n}) => {\n  if (!page.roi.comparison) {\n    return page.roi.afterComparison ? (\n      <article className=\"rounded-2xl bg-white p-6 ring-1 ring-foreground/[0.06] lg:p-8\">\n        <p className=\"font-body text-base leading-relaxed text-foreground/66 lg:text-lg\">\n          {page.roi.afterComparison}\n        </p>\n      </article>\n    ) : null;\n  }\n\n  const options = page.roi.comparison.headers.slice(1);\n  const rows = page.roi.comparison.rows;\n  const investment = rows.find((row) => row[0].toLowerCase().includes('investissement'));\n  const duration = rows.find((row) => row[0].toLowerCase().includes('dur'));\n  const activity = rows.find((row) => row[0].toLowerCase().includes('activ'));\n  const treatmentMidpoint = (treatmentRange.min + treatmentRange.max) / 2;\n\n  return (\n    <div className=\"grid gap-4 lg:grid-cols-3\">\n      {options.map((option, optionIndex) => {\n        const isCovalba = optionIndex === 0;\n        const rawInvestment = investment?.[optionIndex + 1] ?? '';\n        const parsedPrice = parsePricePerM2(rawInvestment);\n        const pricePerM2 = parsedPrice ?? getFallbackPricePerM2(option, optionIndex, treatmentMidpoint);\n        const dynamicInvestment = pricePerM2 ? `~${formatCurrency(surface * pricePerM2)}` : displayCell(rawInvestment);\n\n        return (\n          <article\n            key={option}\n            className={`rounded-2xl p-6 ring-1 ${\n              isCovalba\n                ? 'bg-[#142333] text-white ring-transparent'\n                : 'bg-white text-foreground ring-foreground/[0.06]'\n            }`}\n          >\n            <div className=\"mb-6 flex items-start justify-between gap-4\">\n              <h3 className=\"font-satoshi text-xl font-black leading-tight\">{option}</h3>\n              {isCovalba && (\n                <span className=\"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-teal-vivid/15 text-teal-vivid\">\n                  <Check className=\"h-4 w-4\" strokeWidth={2.4} />\n                </span>\n              )}\n            </div>\n            <p className={`font-satoshi text-3xl font-black leading-none ${isCovalba ? 'text-teal-vivid' : 'text-terracotta'}`}>\n              {dynamicInvestment}\n            </p>\n            {pricePerM2 && (\n              <p className={`mt-2 font-body text-xs font-semibold ${isCovalba ? 'text-white/45' : 'text-foreground/40'}`}>\n                base {new Intl.NumberFormat('fr-FR').format(surface)} m² · {pricePerM2.toLocaleString('fr-FR')} €/m²\n              </p>\n            )}\n            <dl className=\"mt-6 space-y-3 border-t border-current/10 pt-5\">\n              {[duration, activity].filter(Boolean).map((row) => (\n                <div key={row![0]} className=\"grid grid-cols-[0.75fr_1fr] gap-4\">\n                  <dt className={`font-body text-xs font-bold uppercase tracking-[0.14em] ${isCovalba ? 'text-white/38' : 'text-foreground/35'}`}>\n                    {displayCell(row![0])}\n                  </dt>\n                  <dd className={`font-body text-sm leading-relaxed ${isCovalba ? 'text-white/74' : 'text-foreground/62'}`}>\n                    {displayCell(row![optionIndex + 1])}\n                  </dd>\n                </div>\n              ))}\n            </dl>\n          </article>\n        );\n      })}\n    </div>\n  );\n};\n\nconst ROICalculator = ({\n  page,\n  surface,\n  selectedSituation,\n  treatmentRange,\n  onSurfaceChange,\n  onSituationChange,\n}: {\n  page: RoofPageData;\n  surface: number;\n  selectedSituation: string;\n  treatmentRange: ReturnType<typeof getTreatmentRange>;\n  onSurfaceChange: (surface: number) => void;\n  onSituationChange: (situation: string) => void;\n}) => {\n  const treatmentLow = surface * treatmentRange.min;\n  const treatmentHigh = surface * treatmentRange.max;\n  const referencePrice = selectedSituation.toLowerCase().includes('isolation') ? 150 : 90;\n  const referenceCost = surface * referencePrice;\n  const potentialGap = Math.max(referenceCost - treatmentHigh, 0);\n\n  return (\n    <div className=\"mb-10 rounded-[2rem] bg-[#142333] p-5 text-white shadow-[0_34px_90px_-64px_hsl(var(--navy)/0.65)] lg:p-8\">\n      <div className=\"grid gap-7 lg:grid-cols-[0.88fr_1.12fr] lg:items-end\">\n        <div>\n          <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-teal-vivid/70\">\n            Calculateur indicatif\n          </p>\n          <h3 className=\"mt-4 max-w-lg font-satoshi text-2xl font-black leading-tight lg:text-3xl\">\n            Ajustez la surface et l'état de la toiture.\n          </h3>\n          <p className=\"mt-3 max-w-lg font-body text-sm leading-relaxed text-white/52\">\n            Par défaut : 5 000 m², surface type d'un entrepôt ou bâtiment industriel à traiter.\n          </p>\n        </div>\n\n        <div className=\"grid gap-3 sm:grid-cols-[0.72fr_1.28fr]\">\n          <label className=\"block\">\n            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n              Surface\n            </span>\n            <input\n              type=\"number\"\n              min={100}\n              step={100}\n              value={surface}\n              onChange={(event) => onSurfaceChange(Math.max(100, Number(event.target.value) || 100))}\n              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n              aria-label=\"Surface à traiter en mètres carrés\"\n            />\n          </label>\n          <label className=\"block\">\n            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n              Situation\n            </span>\n            <select\n              value={selectedSituation}\n              onChange={(event) => onSituationChange(event.target.value)}\n              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n              aria-label=\"Situation de la toiture\"\n            >\n              {page.roi.situation.rows.map((row) => (\n                <option key={row[0]} value={row[0]} className=\"bg-card text-foreground\">\n                  {row[0]}\n                </option>\n              ))}\n            </select>\n          </label>\n        </div>\n      </div>\n\n      <div className=\"mt-7 grid gap-3 md:grid-cols-3\">\n        {[\n          {\n            label: treatmentRange.label,\n            value: `${formatCurrency(treatmentLow)} - ${formatCurrency(treatmentHigh)}`,\n            detail: `${treatmentRange.min}-${treatmentRange.max} €/m² selon préparation`,\n            valueClass: 'text-teal-vivid',\n          },\n          {\n            label: 'Réfection de référence',\n            value: `~${formatCurrency(referenceCost)}`,\n            detail: `${referencePrice} €/m², ordre de grandeur à comparer`,\n            valueClass: 'text-terracotta',\n          },\n          {\n            label: 'Écart potentiel',\n            value: `jusqu'à ${formatCurrency(potentialGap)}`,\n            detail: \"Investissement lourd évité ou repoussé si le support est viable\",\n            valueClass: 'text-teal-vivid',\n          },\n        ].map((item) => (\n          <article key={item.label} className=\"rounded-2xl bg-white/[0.07] p-5 ring-1 ring-white/10\">\n            <p className=\"font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/38\">{item.label}</p>\n            <p className={`mt-3 font-satoshi text-2xl font-black leading-tight ${item.valueClass}`}>{item.value}</p>\n            <p className=\"mt-2 font-body text-xs leading-relaxed text-white/45\">{item.detail}</p>\n          </article>\n        ))}\n      </div>\n      <p className=\"mt-4 font-body text-xs leading-relaxed text-white/38\">\n        Chiffrage indicatif : l'étude ajuste le prix selon l'accès, la préparation, les points singuliers et l'état réel du support.\n      </p>\n    </div>\n  );\n};\n\nconst getSituationImage = (recommendation: string) => {\n  const lower = recommendation.toLowerCase();\n  if (lower.includes('covametal')) return covaMetalProduct;\n  if (lower.includes('covaseal')) return covaSealProduct;\n  if (lower.includes('cova')) return covaTherm20Product;\n  return covaTherm8Product;\n};\n\nconst SituationCards = ({ rows }: { rows: string[][] }) => (\n  <div className=\"grid gap-4 md:grid-cols-2\">\n    {rows.map((row) => (\n      <article key={row[0]} className=\"grid gap-4 rounded-2xl bg-white p-4 ring-1 ring-foreground/[0.06] sm:grid-cols-[128px_1fr] lg:grid-cols-[190px_1fr] lg:gap-5 lg:p-5\">\n        <div className=\"flex aspect-[4/3] items-center justify-center rounded-xl bg-gradient-to-br from-cream to-white p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.75),_0_18px_44px_-34px_hsl(var(--navy)/0.45)] ring-1 ring-foreground/[0.04] sm:aspect-square lg:p-6\">\n          <img src={getSituationImage(row[1])} alt=\"\" className=\"max-h-full max-w-full object-contain\" loading=\"lazy\" decoding=\"async\" />\n        </div>\n        <div className=\"self-center\">\n          <p className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{row[0]}</p>\n          <p className=\"mt-2 font-body text-sm font-bold text-primary\">{row[1]}</p>\n          <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/58\">{row[2]}</p>\n        </div>\n      </article>\n    ))}\n  </div>\n);\n\nconst ROISection = ({ page }: { page: RoofPageData }) => {\n  const [surface, setSurface] = useState(5000);\n  const [selectedSituation, setSelectedSituation] = useState(page.roi.situation.rows[0]?.[0] ?? '');\n  const treatmentRange = getTreatmentRange(page, selectedSituation);\n\n  return (\n    <section className=\"bg-cream py-20 lg:py-32\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto mb-12 max-w-3xl text-center lg:mb-16\">\n          <div className=\"flex justify-center\">\n            <Label>{page.roi.badge}</Label>\n          </div>\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-[3rem]\">\n            {page.roi.title}\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-foreground/58 lg:text-lg\">\n            {page.roi.intro}\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mb-12 grid gap-4 md:grid-cols-3\">\n          {[\n            ['20-25 €/m²', 'pour traiter et rafraîchir'],\n            ['8-10 ans', 'de réfection repoussée'],\n            ['0 arrêt', \"d'activité pendant le chantier\"],\n          ].map(([value, label]) => (\n            <article key={value} className=\"rounded-2xl bg-white p-6 text-center ring-1 ring-foreground/[0.06]\">\n              <p className=\"font-satoshi text-4xl font-black leading-none text-primary\">{value}</p>\n              <p className=\"mt-3 font-body text-sm font-semibold text-foreground/52\">{label}</p>\n            </article>\n          ))}\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mb-8 max-w-2xl\">\n          <div>\n            <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n              Comparer les options\n            </p>\n            <h3 className=\"mt-4 font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n              Une lecture rapide du budget.\n            </h3>\n          </div>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <ROICalculator\n          page={page}\n          surface={surface}\n          selectedSituation={selectedSituation}\n          treatmentRange={treatmentRange}\n          onSurfaceChange={setSurface}\n          onSituationChange={setSelectedSituation}\n        />\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <RoiOptionCards page={page} surface={surface} treatmentRange={treatmentRange} />\n        {page.roi.comparison && page.roi.afterComparison && (\n          <p className=\"mt-5 max-w-3xl font-body text-sm leading-relaxed text-foreground/52 lg:text-base\">\n            {page.roi.afterComparison}\n          </p>\n        )}\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mt-16 rounded-[2rem] bg-white p-5 ring-1 ring-foreground/[0.06] lg:mt-20 lg:p-8\">\n          <div className=\"mb-6 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n            <div>\n              <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n                Décider selon l'état réel\n              </p>\n              <h3 className=\"mt-4 max-w-xl font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n                Quelle solution pour votre toiture ?\n              </h3>\n            </div>\n            <PrimaryCTA label={page.roi.cta} href=\"/diagnostic\" />\n          </div>\n          <SituationCards rows={page.roi.situation.rows} />\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n  );\n};\n\nconst DiagnosticCTA = ({ title, text }: { title: string; text: string }) => (\n  <section className=\"relative overflow-hidden bg-[#182A3A] py-14 text-white lg:py-20\">\n    <div\n      aria-hidden=\"true\"\n      className=\"absolute inset-0 opacity-35\"\n      style={{\n        backgroundImage:\n          'radial-gradient(circle at 16% 20%, rgba(83, 207, 166, 0.22), transparent 28%), linear-gradient(115deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 120px)',\n      }}\n    />\n    <div className=\"container relative mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto grid max-w-5xl gap-7 lg:grid-cols-[1fr_auto] lg:items-center\">\n          <div>\n            <h2 className=\"font-satoshi text-2xl font-black leading-tight lg:text-4xl\">{title}</h2>\n            <p className=\"mt-3 max-w-2xl font-body text-sm leading-relaxed text-white/68 lg:text-base\">{text}</p>\n          </div>\n          <div className=\"flex flex-col gap-3 sm:flex-row lg:justify-end\">\n            <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n          </div>\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst MethodSection = ({ page }: { page: RoofPageData }) => {\n  const [videoOpen, setVideoOpen] = useState(false);\n\n  return (\n    <section className=\"bg-white py-16 lg:py-32\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n        <div className=\"flex flex-col items-start gap-12 lg:flex-row lg:gap-20\">\n          <div className=\"lg:sticky lg:top-[120px] lg:w-[40%]\">\n            <ScrollReveal>\n              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/50\">\n                Le process\n              </p>\n              <h2\n                className=\"mb-6 font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                4 étapes. Zéro interruption.\n                <span className=\"text-foreground/30\"> Zéro risque.</span>\n              </h2>\n              <p className=\"mb-8 font-body text-base leading-relaxed text-muted-foreground lg:text-lg\">\n                {page.method.reassurance}\n              </p>\n              <button\n                onClick={() => setVideoOpen(true)}\n                className=\"group flex items-center gap-3 text-primary transition-colors hover:text-primary\"\n              >\n                <span className=\"flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 transition-colors group-hover:bg-primary/20\">\n                  <Play className=\"h-5 w-5\" />\n                </span>\n                <span className=\"font-body text-sm font-semibold\">Voir un chantier en 2 minutes</span>\n              </button>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"relative lg:w-[60%]\">\n            <div className=\"absolute bottom-4 left-5 top-4 w-px bg-gradient-to-b from-primary/25 via-primary/10 to-transparent lg:left-6\" />\n            <div className=\"flex flex-col\">\n              {page.method.steps.map((step, index) => {\n                const Icon = methodIcons[index] ?? CheckCircle2;\n                return (\n                  <ScrollReveal key={step.title}>\n                    <div className=\"group relative flex items-start gap-5 py-8 lg:gap-7 lg:py-10\">\n                      <span className=\"relative z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-primary/20 bg-primary/10 transition-colors duration-500 group-hover:bg-primary/15 lg:h-12 lg:w-12\">\n                        <Icon className=\"h-4 w-4 text-primary lg:h-5 lg:w-5\" />\n                      </span>\n                      <div className=\"pt-1\">\n                        <span className=\"mb-2 block font-body text-xs font-bold uppercase tracking-widest text-primary/50\">\n                          Étape 0{index + 1}\n                        </span>\n                        <h3 className=\"mb-2 font-satoshi text-xl font-bold text-foreground lg:text-2xl\">\n                          {step.title}\n                        </h3>\n                        <p className=\"max-w-md font-body text-sm leading-relaxed text-muted-foreground lg:text-base\">\n                          {step.text}\n                        </p>\n                      </div>\n                    </div>\n                    {index < page.method.steps.length - 1 && <div className=\"ml-5 h-px bg-border lg:ml-6\" />}\n                  </ScrollReveal>\n                );\n              })}\n            </div>\n\n            <ScrollReveal>\n              <div className=\"ml-16 mt-4 lg:ml-20\">\n                <p className=\"font-body text-xs font-medium text-muted-foreground/50\">\n                  Durée moyenne : 3 jours · Sans arrêt d'activité\n                </p>\n              </div>\n            </ScrollReveal>\n          </div>\n        </div>\n      </div>\n\n      <Dialog open={videoOpen} onOpenChange={setVideoOpen}>\n        <DialogContent className=\"max-w-4xl overflow-hidden border-none bg-black p-0\">\n          <div className=\"aspect-video\">\n            <iframe\n              src=\"https://www.youtube.com/embed/9HOB5OVH0Ac?autoplay=1\"\n              className=\"h-full w-full\"\n              allow=\"autoplay; encrypted-media\"\n              allowFullScreen\n              title=\"Covalba Cool Roof, un chantier en 2 minutes\"\n            />\n          </div>\n        </DialogContent>\n      </Dialog>\n    </section>\n  );\n};\n\nconst ProofSection = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-[#192A3A] py-20 text-white lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mb-12 grid gap-8 lg:grid-cols-[0.9fr_1.1fr] lg:items-end\">\n          <div>\n            <Label light>{page.proof.badge}</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight sm:text-4xl lg:text-5xl\">{page.proof.title}</h2>\n          </div>\n          <p className=\"max-w-xl font-body text-base leading-relaxed text-white/62\">\n            Des chiffres de terrain à confirmer chantier par chantier, utilisés ici comme repères de performance.\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal stagger>\n        <div className=\"grid gap-4 md:grid-cols-3\">\n          {page.proof.figures.map((figure, index) => {\n            const Icon = proofIcons[index] ?? BadgeCheck;\n            return (\n              <article key={figure.value} className=\"rounded-2xl border border-white/10 bg-white/[0.045] p-7\">\n                <Icon className=\"mb-6 h-6 w-6 text-teal-vivid\" strokeWidth={1.7} />\n                <p className=\"font-satoshi text-3xl font-black leading-none text-white lg:text-4xl\">{figure.value}</p>\n                <p className=\"mt-3 font-body text-sm leading-relaxed text-white/60\">{figure.label}</p>\n              </article>\n            );\n          })}\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mt-14 border-t border-white/10 pt-10\">\n          <p className=\"mb-8 text-center font-body text-[10px] font-bold uppercase tracking-[0.22em] text-white/42\">\n            Références clients\n          </p>\n          <div className=\"flex flex-wrap items-center justify-center gap-x-10 gap-y-7 lg:gap-x-14\">\n            {logos.map((logo) => (\n              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n                <img\n                  src={logo.url}\n                  alt={logo.name}\n                  className=\"max-h-7 max-w-full object-contain opacity-45 transition-opacity duration-300 hover:opacity-80\"\n                  style={{ filter: 'brightness(0) invert(1)' }}\n                  loading=\"lazy\"\n                />\n              </span>\n            ))}\n          </div>\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst FAQSection = ({ faq }: { faq: RoofPageData['faq'] }) => (\n  <section className=\"bg-cream py-20 lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <div className=\"grid gap-10 lg:grid-cols-[0.85fr_1.15fr] lg:gap-16\">\n        <ScrollReveal>\n          <div className=\"lg:sticky lg:top-24\">\n            <Label>FAQ</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n              Vos questions,\n              <br />\n              <span className=\"text-foreground/40\">nos réponses.</span>\n            </h2>\n            <a href=\"#contact\" className=\"group mt-8 inline-flex items-center gap-2 font-body text-sm font-semibold text-primary transition-all hover:gap-3\">\n              Une autre question ? Parlons-en\n              <ArrowRight className=\"h-4 w-4\" />\n            </a>\n          </div>\n        </ScrollReveal>\n\n        <ScrollReveal>\n          <Accordion type=\"single\" collapsible className=\"space-y-3\">\n            {faq.map((item, index) => (\n              <AccordionItem\n                key={item.q}\n                value={`roof-faq-${index}`}\n                className=\"rounded-xl border border-foreground/[0.06] bg-white px-5 shadow-[0_12px_34px_rgba(24,42,58,0.04)] data-[state=open]:border-primary/20 lg:px-6\"\n              >\n                <AccordionTrigger className=\"py-5 text-left font-satoshi text-base font-bold text-foreground hover:no-underline lg:text-[17px]\">\n                  {item.q}\n                </AccordionTrigger>\n                <AccordionContent className=\"pb-5 font-body text-[14.5px] leading-relaxed text-foreground/67 lg:text-[15px]\">\n                  {item.a}\n                </AccordionContent>\n              </AccordionItem>\n            ))}\n          </Accordion>\n        </ScrollReveal>\n      </div>\n    </div>\n  </section>\n);\n\nconst FinalCTA = ({ page }: { page: RoofPageData }) => (\n  <section id=\"contact\" className=\"overflow-hidden bg-[#182A3A] py-20 text-white lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto max-w-4xl text-center\">\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-white sm:text-4xl lg:text-5xl\">\n            {page.finalCta.title}\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-white/72 lg:text-lg\">\n            {page.finalCta.text}\n          </p>\n          <div className=\"mt-9 flex flex-col gap-3 sm:flex-row sm:justify-center\">\n            <PrimaryCTA label=\"Demander un devis\" href=\"/diagnostic\" />\n          </div>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <ul className=\"mx-auto mt-12 grid max-w-3xl gap-3 sm:grid-cols-3\">\n          {page.finalCta.reassurances.map((item) => (\n            <li key={item} className=\"flex items-center justify-center gap-2.5 rounded-lg border border-white/[0.12] bg-white/[0.045] px-4 py-3\">\n              <CalendarCheck className=\"h-4 w-4 shrink-0 text-teal-vivid\" strokeWidth={1.8} />\n              <span className=\"font-body text-sm text-white/[0.76]\">{item}</span>\n            </li>\n          ))}\n        </ul>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst SEOContent = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-white py-20 lg:py-28\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <article className=\"mx-auto max-w-3xl\">\n          <p className=\"mb-5 font-body text-[10px] font-bold uppercase tracking-[0.25em] text-primary/55\">\n            Guide support\n          </p>\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground lg:text-4xl\">\n            Tout comprendre sur le {page.eyebrow.toLowerCase()}.\n          </h2>\n          <p className=\"mt-6 font-body text-base leading-relaxed text-foreground/68 lg:text-lg\">\n            {page.seo.intro}\n          </p>\n\n          <div className=\"mt-10 space-y-9\">\n            {page.seo.sections.map((section) => (\n              <section key={section.title}>\n                <h3 className=\"font-satoshi text-2xl font-bold leading-tight text-foreground\">{section.title}</h3>\n                <p className=\"mt-3 font-body text-base leading-relaxed text-foreground/66\">{section.text}</p>\n              </section>\n            ))}\n          </div>\n\n          <div className=\"mt-12 rounded-2xl bg-cream p-6 ring-1 ring-foreground/[0.05]\">\n            <h3 className=\"font-satoshi text-lg font-bold text-foreground\">Sources</h3>\n            <ul className=\"mt-4 space-y-2\">\n              {page.seo.sources.map((source) => (\n                <li key={source.href}>\n                  <a href={source.href} className=\"font-body text-sm leading-relaxed text-primary underline-offset-4 hover:underline\" target=\"_blank\" rel=\"noreferrer\">\n                    {source.label}\n                  </a>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </article>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst buildFaqSchema = (page: RoofPageData) => ({\n  '@context': 'https://schema.org',\n  '@type': 'FAQPage',\n  mainEntity: page.faq.map((item) => ({\n    '@type': 'Question',\n    name: item.q,\n    acceptedAnswer: {\n      '@type': 'Answer',\n      text: item.a,\n    },\n  })),\n});\n\nconst buildBreadcrumbSchema = (page: RoofPageData) => ({\n  '@context': 'https://schema.org',\n  '@type': 'BreadcrumbList',\n  itemListElement: [\n    { '@type': 'ListItem', position: 1, name: 'Accueil', item: SITE_URL },\n    { '@type': 'ListItem', position: 2, name: 'Toitures', item: `${SITE_URL}/toitures` },\n    { '@type': 'ListItem', position: 3, name: page.breadcrumb, item: page.canonical },\n  ],\n});\n\nconst RoofPageTemplate = ({ page }: RoofPageTemplateProps) => {\n  usePageMeta({\n    title: page.title,\n    description: page.description,\n    canonical: page.canonical,\n  });\n  useJsonLd(`roof-${page.slug}-faq`, buildFaqSchema(page));\n  useJsonLd(`roof-${page.slug}-breadcrumb`, buildBreadcrumbSchema(page));\n\n  return (\n    <>\n      <Navbar />\n      <main>\n        <RoofHero page={page} />\n        <ProblemSection page={page} />\n        <SolutionSection page={page} />\n        <BenefitsSection page={page} />\n        <ROISection page={page} />\n        <DiagnosticCTA title={page.midCta.title} text={page.midCta.text} />\n        <MethodSection page={page} />\n        <ProofSection page={page} />\n        <FAQSection faq={page.faq} />\n        <FinalCTA page={page} />\n        <SEOContent page={page} />\n      </main>\n      <Footer />\n      <StickyMobileCTA />\n    </>\n  );\n};\n\nexport default RoofPageTemplate;\n","numLines":1427,"startLine":1,"totalLines":1427}},"sourceToolAssistantUUID":"a1528cbd-6429-4748-a883-6f3975648ecd","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"ecc7cdb0-ff4d-4929-95c8-8117a4255a95","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01XJHWPF6dBuno3wWfGyKLmw","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_018oX1KnhyauC4domVFykhC2","name":"Read","input":{"file_path":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/data/roofPages.ts"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":23595,"cache_creation_input_tokens":15098,"cache_read_input_tokens":0,"output_tokens":208,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":15098},"inference_geo":"not_available","iterations":[{"input_tokens":23595,"output_tokens":208,"cache_read_input_tokens":0,"cache_creation_input_tokens":15098,"cache_creation":{"ephemeral_5m_input_tokens":15098,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CcHKSPVR3kb4mD8CkBg4N","attributionAgent":"general-purpose","type":"assistant","uuid":"cf40b125-c7ac-455b-b570-ebe89e9dee00","timestamp":"2026-06-22T00:03:23.707Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"cf40b125-c7ac-455b-b570-ebe89e9dee00","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_018oX1KnhyauC4domVFykhC2","type":"tool_result","content":"1\timport covaSealHero from '@/assets/produits/covaseal-hero.webp';\n2\timport compatBacAcier from '@/assets/produits/compat-bac-acier.webp';\n3\timport compatBitume from '@/assets/produits/compat-bitume.webp';\n4\timport compatFibrociment from '@/assets/produits/compat-fibrociment.webp';\n5\timport compatTuile from '@/assets/produits/compat-tuile.webp';\n6\t\n7\texport interface TextCard {\n8\t  title: string;\n9\t  text: string;\n10\t}\n11\t\n12\texport interface TableBlock {\n13\t  headers: string[];\n14\t  rows: string[][];\n15\t}\n16\t\n17\texport interface RoofPageData {\n18\t  slug: string;\n19\t  path: string;\n20\t  title: string;\n21\t  description: string;\n22\t  canonical: string;\n23\t  eyebrow: string;\n24\t  breadcrumb: string;\n25\t  h1: string;\n26\t  lead: string;\n27\t  heroImage: string;\n28\t  heroImageAlt: string;\n29\t  heroStats: { value: string; label: string }[];\n30\t  reassurance: string[];\n31\t  problem: {\n32\t    badge: string;\n33\t    title: string;\n34\t    cards: TextCard[];\n35\t    transition: string;\n36\t  };\n37\t  solution: {\n38\t    badge: string;\n39\t    title: string;\n40\t    text: string;\n41\t    systemTitle: string;\n42\t    system: string[];\n43\t    note?: string;\n44\t    cta: { label: string; href: string };\n45\t  };\n46\t  benefits: {\n47\t    badge: string;\n48\t    title: string;\n49\t    featured: string;\n50\t    cards: TextCard[];\n51\t  };\n52\t  roi: {\n53\t    badge: string;\n54\t    title: string;\n55\t    intro: string;\n56\t    comparison?: TableBlock;\n57\t    afterComparison?: string;\n58\t    situation: TableBlock;\n59\t    cta: string;\n60\t  };\n61\t  midCta: {\n62\t    title: string;\n63\t    text: string;\n64\t  };\n65\t  compatibility: {\n66\t    badge: string;\n67\t    title: string;\n68\t    text: string;\n69\t    image: string;\n70\t  };\n71\t  method: {\n72\t    badge: string;\n73\t    title: string;\n74\t    steps: TextCard[];\n75\t    reassurance: string;\n76\t  };\n77\t  proof: {\n78\t    badge: string;\n79\t    title: string;\n80\t    figures: { value: string; label: string }[];\n81\t  };\n82\t  faq: { q: string; a: string }[];\n83\t  finalCta: {\n84\t    title: string;\n85\t    text: string;\n86\t    reassurances: string[];\n87\t  };\n88\t  seo: {\n89\t    intro: string;\n90\t    sections: TextCard[];\n91\t    sources: { label: string; href: string }[];\n92\t  };\n93\t  productName: string;\n94\t}\n95\t\n96\tconst commonSources = [\n97\t  { label: 'Lawrence Berkeley National Laboratory, Heat Island Group, Cool Roofs', href: 'https://heatisland.lbl.gov/coolscience/cool-roofs' },\n98\t  { label: 'U.S. Environmental Protection Agency, Using Cool Roofs to Reduce Heat Islands', href: 'https://www.epa.gov/heatislands/using-cool-roofs-reduce-heat-islands' },\n99\t  { label: 'EPA, Reducing Urban Heat Islands: Compendium of Strategies, Cool Roofs', href: 'https://www.epa.gov/sites/default/files/2014-06/documents/coolroofscompendium.pdf' },\n100\t  { label: 'ADEME, ilots de chaleur urbains et rafraichissement passif', href: 'https://www.ademe.fr' },\n101\t];\n102\t\n103\tconst defaultRoiComparison: TableBlock = {\n104\t  headers: ['', 'Cool roof Covalba', 'Membrane neuve blanche', 'Remplacement isolation + etancheite'],\n105\t  rows: [\n106\t    ['Investissement', '~112 500 EUR (22,5 EUR/m2)', '~450 000 EUR (90 EUR/m2)', '~750 000 EUR (150 EUR/m2)'],\n107\t    ['Duree de vie', '8-10 ans', '15-20 ans', '20+ ans'],\n108\t    ['Investissement decale', 'reference', \"jusqu'a +337 500 EUR\", \"jusqu'a +637 500 EUR\"],\n109\t    ['Activite pendant le chantier', 'maintenue', 'interrompue', 'interrompue'],\n110\t  ],\n111\t};\n112\t\n113\texport const roofPages: RoofPageData[] = [\n114\t  {\n115\t    slug: 'membrane-bitumineuse',\n116\t    path: '/toitures/membrane-bitumineuse',\n117\t    title: 'Cool roof membrane bitumineuse : -10°C et étanchéité prolongée',\n118\t    description: \"Cool roof pour toiture en membrane bitumineuse : -8 à -10°C sous toiture et 8 à 10 ans d'étanchéité gagnés, pour 20-25 €/m².\",\n119\t    canonical: 'https://www.covalba.fr/toitures/membrane-bitumineuse',\n120\t    eyebrow: 'COOL ROOF MEMBRANE BITUMINEUSE',\n121\t    breadcrumb: 'Membrane bitumineuse',\n122\t    h1: 'Cool roof pour toiture en membrane bitumineuse',\n123\t    lead: \"Rafraîchissez votre toiture bitume de 8 à 10°C et prolongez son étanchéité, sans la remplacer.\",\n124\t    heroImage: compatBitume,\n125\t    heroImageAlt: 'Toiture terrasse en membrane bitumineuse compatible avec un traitement cool roof',\n126\t    heroStats: [\n127\t      { value: '-8 à -10°C', label: 'sous toiture' },\n128\t      { value: '+8 à 10 ans', label: \"d'étanchéité\" },\n129\t      { value: '20-25 €/m²', label: 'contre 80-100 €' },\n130\t    ],\n131\t    reassurance: ['Pose sécurisée, machines au pied', 'Compatible bitume et auto-protégé', 'Tenue 8 à 10 ans', 'Sans interrompre votre activité'],\n132\t    problem: {\n133\t      badge: 'Pourquoi agir',\n134\t      title: \"Une toiture bitume noire, c'est un radiateur posé sur votre bâtiment.\",\n135\t      cards: [\n136\t        { title: 'Elle absorbe tout le soleil', text: \"Une membrane bitumineuse sombre peut grimper à 80°C en surface l'été. Cette chaleur traverse la toiture et fait monter la température à l'intérieur.\" },\n137\t        { title: 'Elle vieillit plus vite', text: \"Les UV et les chocs thermiques font farinier puis fissurer le bitume. Plus la membrane chauffe, plus elle se fragilise.\" },\n138\t        { title: 'Elle pèse sur votre confort et votre facture', text: \"Sous une toiture surchauffée, les équipes peinent et la climatisation, là où il y en a, tourne en surrégime.\" },\n139\t      ],\n140\t      transition: 'Bonne nouvelle, on traite la cause sans toucher à la structure.',\n141\t    },\n142\t    solution: {\n143\t      badge: 'La solution Covalba',\n144\t      title: \"Votre toiture renvoie le soleil au lieu de l'absorber.\",\n145\t      text: \"Sur une membrane bitumineuse, Covalba applique le système CovaTherm : un revêtement réfléchissant clair qui renvoie jusqu'à 90 % du rayonnement solaire. La membrane ne chauffe plus, le bâtiment reste tempéré, et l'étanchéité existante est protégée des UV. Si elle est déjà fatiguée, CovaSeal 20 refait l'étanchéité et rafraîchit en une seule opération.\",\n146\t      systemTitle: 'Le système en 3 couches',\n147\t      system: [\"Primaire d'accroche adapté au bitume\", 'Base coat CovaTherm 8 en polyuréthane', 'Top coat CovaTherm 20 pour la tenue dans le temps'],\n148\t      cta: { label: 'Voir la fiche CovaTherm', href: '/solutions/covatherm' },\n149\t    },\n150\t    benefits: {\n151\t      badge: 'Ce que vous y gagnez',\n152\t      title: 'Plus frais dessous, plus durable dessus.',\n153\t      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n154\t      cards: [\n155\t        { title: 'Un bâtiment tempéré', text: 'La chaleur ne passe plus par le toit. Les derniers niveaux redeviennent confortables sans climatiser davantage.' },\n156\t        { title: 'Une étanchéité prolongée', text: 'Protégée des UV et des chocs thermiques, votre membrane gagne 8 à 10 ans de durée de vie.' },\n157\t        { title: 'Une facture allégée', text: 'Sur les bâtiments climatisés, les groupes de froid sont moins sollicités.' },\n158\t        { title: 'Un gros chantier repoussé', text: 'Vous décalez le remplacement de toiture, un investissement bien plus lourd.' },\n159\t      ],\n160\t    },\n161\t    roi: {\n162\t      badge: 'Le bon calcul',\n163\t      title: 'Repousser la réfection, sans immobiliser le bâtiment.',\n164\t      intro: \"Tant que la membrane reste viable, le cool roof traite la chaleur et protège l'étanchéité pour une fraction du prix d'une réfection.\",\n165\t      comparison: defaultRoiComparison,\n166\t      afterComparison: \"La logique est simple : moins de chaleur dès cet été, et une grosse dépense repoussée.\",\n167\t      situation: {\n168\t        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n169\t        rows: [\n170\t          ['Membrane saine exposée à la chaleur', 'Cool roof CovaTherm', \"Investissement minimal, confort immédiat, étanchéité prolongée.\"],\n171\t          ['Membrane vieillissante', 'CovaSeal 20', \"Refait l'étanchéité et rafraîchit pour environ un quart du prix d'une membrane neuve.\"],\n172\t          ['Fuites actives', 'Reprise des points singuliers puis CovaSeal 20, ou membrane neuve si trop dégradée', \"On ne peint pas sur une étanchéité morte. On vous le dit franchement.\"],\n173\t          ['Isolation insuffisante', \"Cool roof pour l'été, à comparer avec une réfection complète si l'isolation est le vrai sujet\", \"Le cool roof agit sur l'été, il ne remplace pas l'isolation.\"],\n174\t        ],\n175\t      },\n176\t      cta: 'Demander un devis',\n177\t    },\n178\t    midCta: {\n179\t      title: 'Gagnez de la fraîcheur sans refaire votre toiture bitume.',\n180\t      text: \"On vérifie l'état de la membrane, on chiffre le gain thermique possible et on vous dit si CovaTherm suffit ou si l'étanchéité doit être reprise.\",\n181\t    },\n182\t    compatibility: {\n183\t      badge: 'Sur quoi on intervient',\n184\t      title: 'Compatible avec votre membrane, après étude technique.',\n185\t      text: \"Le système s'applique sur la plupart des membranes bitumineuses : bitume nu, membrane auto-protégée à paillettes ardoisées, multicouche. Tout commence par une étude de l'état de l'étanchéité et des points singuliers. La toiture est nettoyée, les points singuliers sont repris si besoin, puis le primaire garantit l'accroche avant l'application.\",\n186\t      image: compatBitume,\n187\t    },\n188\t    method: {\n189\t      badge: 'Chantier en sécurité',\n190\t      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n191\t      steps: [\n192\t        { title: 'Étude technique', text: 'État de la membrane, points singuliers, objectifs thermiques.' },\n193\t        { title: 'Préparation', text: 'Nettoyage haute pression et reprise des points sensibles.' },\n194\t        { title: 'Application sécurisée', text: \"Projection depuis l'extérieur, seuls les flexibles montent en toiture.\" },\n195\t        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n196\t      ],\n197\t      reassurance: \"Machines au pied du bâtiment, rien sur le toit. Aucun risque pour la structure ni pour l'étanchéité, applicateurs formés et équipés.\",\n198\t    },\n199\t    proof: {\n200\t      badge: 'Des résultats concrets',\n201\t      title: 'Mesuré sur le terrain.',\n202\t      figures: [\n203\t        { value: '-7,5°C', label: 'sur la température de surface' },\n204\t        { value: '+8 à 10 ans', label: \"d'étanchéité préservée\" },\n205\t        { value: '80 %', label: 'de nos chantiers réalisés sur membrane bitumineuse' },\n206\t      ],\n207\t    },\n208\t    faq: [\n209\t      { q: 'Mon étanchéité est vieille, dois-je la refaire avant ?', a: \"Pas forcément. Si la membrane est saine, le cool roof la protège et la prolonge. Si elle farine ou présente des microfissures sans fuite, CovaSeal 20 refait l'étanchéité et rafraîchit en une seule opération. L'étude technique tranche.\" },\n210\t      { q: 'Ça tient sur une membrane à paillettes ardoisées ?', a: \"Oui, après préparation adaptée et primaire d'accroche. C'est l'un des supports les plus courants que nous traitons.\" },\n211\t      { q: \"Combien de temps ça prolonge mon étanchéité ?\", a: \"En la protégeant des UV et des chocs thermiques, le cool roof allonge sa durée de vie de 8 à 10 ans en général.\" },\n212\t      { q: \"Faut-il décaper l'ancienne membrane ?\", a: \"Non. Un nettoyage haute pression suffit, suivi de la reprise des points singuliers si nécessaire.\" },\n213\t      { q: 'Et les fuites existantes ?', a: \"Elles se traitent avant toute application. Le cool roof n'est pas une réparation d'étanchéité, c'est une protection.\" },\n214\t      { q: \"Et l'hiver, je vais consommer plus de chauffage ?\", a: \"L'effet est minime. En hiver le soleil est bas et peu présent. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n215\t    ],\n216\t    finalCta: {\n217\t      title: 'Rafraîchissez votre toiture bitume sans la refaire.',\n218\t      text: \"CovaTherm protège votre membrane, réduit la chaleur sous toiture et repousse une réfection lourde tant que l'étanchéité est viable.\",\n219\t      reassurances: ['Étude sous 48h', '80 % de nos chantiers sur bitume', 'Sans engagement'],\n220\t    },\n221\t    seo: {\n222\t      intro: \"La membrane bitumineuse est le revêtement d'étanchéité le plus répandu sur les toitures-terrasses des bâtiments industriels et tertiaires. Robuste et éprouvée, elle a un défaut : sa couleur sombre en fait un capteur de chaleur.\",\n223\t      sections: [\n224\t        { title: 'Pourquoi une toiture en membrane bitumineuse surchauffe', text: \"Le bitume est foncé, et une surface foncée absorbe l'essentiel du rayonnement solaire qu'elle reçoit. En plein été, une membrane bitumineuse peut atteindre 80°C en surface. Cette chaleur traverse ensuite la toiture et se diffuse à l'intérieur du bâtiment.\" },\n225\t        { title: 'Le cool roof sur membrane bitumineuse, comment ça marche', text: \"Le cool roof consiste à appliquer un revêtement clair à haute réflectance solaire directement sur l'étanchéité existante. Covalba utilise le système CovaTherm : primaire, base coat polyuréthane et top coat de finition.\" },\n226\t        { title: \"Peinture cool roof ou réfection d'étanchéité : le bon calcul\", text: \"Refaire une étanchéité coûte entre 80 et 100 €/m² pour une membrane neuve, et jusqu'à 150 €/m² si l'isolation est reprise. Un cool roof se situe autour de 20 à 25 €/m² et repousse le remplacement.\" },\n227\t        { title: 'Quand opter pour CovaSeal 20', text: \"Lorsque l'étanchéité vieillit, farine ou présente des microfissures sans fuite, CovaSeal 20 renforce l'étanchéité existante et rafraîchit la toiture en une seule opération.\" },\n228\t        { title: 'Un principe validé par la recherche', text: \"Les laboratoires publics documentent depuis longtemps l'efficacité des toitures réfléchissantes. Ces ordres de grandeur expliquent les gains mesurés sur les grandes toitures industrielles.\" },\n229\t        { title: 'Une protection qui dure', text: \"Le système CovaTherm, en polyuréthane avec vernis de finition, est conçu pour conserver ses performances 8 à 10 ans. C'est cette longévité qui transforme le cool roof en investissement.\" },\n230\t      ],\n231\t      sources: commonSources,\n232\t    },\n233\t    productName: 'CovaTherm',\n234\t  },\n235\t  {\n236\t    slug: 'bac-acier',\n237\t    path: '/toitures/bac-acier',\n238\t    title: 'Cool roof bac acier : -10°C et anticorrosion | Covalba',\n239\t    description: 'Cool roof pour toiture bac acier : -8 à -10°C sous toiture et protection anticorrosion en une passe, pour 20-25 €/m².',\n240\t    canonical: 'https://www.covalba.fr/toitures/bac-acier',\n241\t    eyebrow: 'COOL ROOF BAC ACIER',\n242\t    breadcrumb: 'Bac acier',\n243\t    h1: 'Cool roof pour toiture en bac acier',\n244\t    lead: 'Rafraîchissez votre toiture bac acier de 8 à 10°C et stoppez la corrosion, sans changer la tôle.',\n245\t    heroImage: compatBacAcier,\n246\t    heroImageAlt: 'Toiture en bac acier exposée au soleil avant traitement cool roof anticorrosion',\n247\t    heroStats: [\n248\t      { value: '-8 à -10°C', label: 'sous toiture' },\n249\t      { value: 'Anticorrosion', label: '+ réflectif en une passe' },\n250\t      { value: '20-25 €/m²', label: 'sans dépose de tôle' },\n251\t    ],\n252\t    reassurance: ['Pose sécurisée, machines au pied', 'Compatible acier, galva et laqué', 'Traite corrosion et chaleur', 'Sans interrompre votre activité'],\n253\t    problem: {\n254\t      badge: 'Pourquoi agir',\n255\t      title: 'Le bac acier chauffe vite, rouille vite, et fatigue vite.',\n256\t      cards: [\n257\t        { title: 'Le métal conduit la chaleur', text: \"Une tôle sombre monte très haut en température au soleil et transmet cette chaleur à l'intérieur presque instantanément.\" },\n258\t        { title: \"La corrosion s'installe\", text: \"Aux recouvrements, aux fixations et là où la peinture d'origine s'efface, la rouille gagne et menace l'étanchéité.\" },\n259\t        { title: 'Dilatation et condensation', text: 'Les écarts de température font travailler la tôle et favorisent la condensation côté intérieur.' },\n260\t      ],\n261\t      transition: 'Une seule intervention peut traiter la chaleur et la corrosion en même temps.',\n262\t    },\n263\t    solution: {\n264\t      badge: 'La solution Covalba',\n265\t      title: 'Votre toiture renvoie le soleil et cesse de rouiller.',\n266\t      text: \"Sur une toiture bac acier, Covalba applique CovaMetal 20, une peinture anticorrosion réfléchissante : elle protège le métal de la rouille tout en renvoyant le rayonnement solaire. Une finition CovaTherm complète le système pour une réflectivité durable.\",\n267\t      systemTitle: 'Le système',\n268\t      system: ['Préparation et traitement des points de corrosion', 'CovaMetal 20 anticorrosion réfléchissant', 'Finition réflective CovaTherm pour la tenue dans le temps'],\n269\t      cta: { label: 'Voir la fiche CovaMetal 20', href: '/solutions/covametal-20' },\n270\t    },\n271\t    benefits: {\n272\t      badge: 'Ce que vous y gagnez',\n273\t      title: 'Plus frais, protégé de la rouille, prolongé.',\n274\t      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n275\t      cards: [\n276\t        { title: 'Un atelier ou un entrepôt tempéré', text: 'La tôle ne transmet plus sa chaleur. Les espaces sous bac acier redeviennent supportables l’été.' },\n277\t        { title: 'La corrosion stoppée', text: 'Le métal est protégé, la rouille existante traitée, la progression bloquée.' },\n278\t        { title: 'Une tôle prolongée', text: 'En traitant corrosion et chaleur, vous repoussez de plusieurs années le remplacement de la couverture.' },\n279\t        { title: 'Une facture allégée', text: 'Sur les bâtiments climatisés, les groupes de froid travaillent moins.' },\n280\t      ],\n281\t    },\n282\t    roi: {\n283\t      badge: 'Le bon calcul',\n284\t      title: 'Traiter la tôle avant de devoir la changer.',\n285\t      intro: 'Tant que le métal reste sain, le traitement anticorrosion réfléchissant protège, rafraîchit et évite une dépose lourde.',\n286\t      comparison: {\n287\t        headers: ['', 'Cool roof CovaMetal 20', 'Repeindre en anticorrosion simple', 'Remplacement de la couverture'],\n288\t        rows: [\n289\t          ['Investissement', '~112 500 EUR (22,5 EUR/m2)', 'moins cher au m2', 'bien plus élevé'],\n290\t          ['Rafraîchit le bâtiment', 'oui (-8 à -10°C)', 'non', 'selon le produit posé'],\n291\t          ['Protège de la corrosion', 'oui', 'oui', 'neuf'],\n292\t          ['Activité pendant le chantier', 'maintenue', 'maintenue', 'interrompue'],\n293\t        ],\n294\t      },\n295\t      afterComparison: 'La différence se joue sur le double bénéfice : corrosion traitée et chaleur réduite.',\n296\t      situation: {\n297\t        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n298\t        rows: [\n299\t          ['Tôle saine exposée à la chaleur', 'Cool roof CovaTherm', 'Le métal est bon, on rafraîchit simplement et durablement.'],\n300\t          ['Corrosion qui démarre', 'CovaMetal 20 + finition CovaTherm', 'Corrosion et chaleur traitées en une passe, tôle prolongée.'],\n301\t          ['Corrosion perforante / tôle percée', \"Remplacement des tôles touchées, puis traitement de l'ensemble\", \"On ne peint pas sur une tôle morte. On reprend d'abord le ponctuel.\"],\n302\t          ['Forte condensation', 'Étude préalable', 'Traiter la cause avant la surface.'],\n303\t        ],\n304\t      },\n305\t      cta: 'Demander un devis',\n306\t    },\n307\t    midCta: {\n308\t      title: 'Refroidissez votre bac acier tout en bloquant la corrosion.',\n309\t      text: \"On vérifie l'état du métal, les fixations et les départs de rouille pour chiffrer un traitement qui protège et rafraîchit en une opération.\",\n310\t    },\n311\t    compatibility: {\n312\t      badge: 'Sur quoi on intervient',\n313\t      title: 'Compatible acier, galvanisé et laqué, après étude technique.',\n314\t      text: \"Le système s'applique sur les couvertures et bardages en bac acier, qu'ils soient bruts, galvanisés ou prélaqués. On commence par une étude de l'état du métal et des points de corrosion. La rouille est traitée localement, puis CovaMetal 20 est appliqué avant la finition réflective.\",\n315\t      image: compatBacAcier,\n316\t    },\n317\t    method: {\n318\t      badge: 'Chantier en sécurité',\n319\t      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n320\t      steps: [\n321\t        { title: 'Étude technique', text: 'État du métal, corrosion, points singuliers, objectifs thermiques.' },\n322\t        { title: 'Préparation', text: 'Nettoyage et traitement des points de corrosion.' },\n323\t        { title: 'Application sécurisée', text: \"CovaMetal 20 puis finition, projection depuis l'extérieur.\" },\n324\t        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n325\t      ],\n326\t      reassurance: \"Machines au pied du bâtiment, rien sur le toit. Aucun risque pour la structure ni pour l'étanchéité, applicateurs formés et équipés.\",\n327\t    },\n328\t    proof: {\n329\t      badge: 'Des résultats concrets',\n330\t      title: 'Mesuré sur le terrain.',\n331\t      figures: [\n332\t        { value: '-7,5°C', label: 'sur la température de surface' },\n333\t        { value: 'Corrosion stoppée', label: 'sur les zones traitées' },\n334\t        { value: '20 %', label: 'de nos chantiers réalisés sur bac acier' },\n335\t      ],\n336\t    },\n337\t    faq: [\n338\t      { q: \"Ma tôle est déjà rouillée, c'est trop tard ?\", a: \"Non, tant que la corrosion n'a pas perforé le métal. On traite la rouille existante avant d'appliquer CovaMetal 20.\" },\n339\t      { q: 'Ça tient sur du galvanisé ou du laqué ?', a: \"Oui, après préparation adaptée. L'étude technique vérifie l'adhérence et définit la bonne préparation selon le revêtement d'origine.\" },\n340\t      { q: 'Le cool roof réduit-il la condensation ?', a: \"Il agit surtout sur la chaleur d'été. La condensation dépend aussi de la ventilation et de l'isolation.\" },\n341\t      { q: 'Et les recouvrements et les fixations ?', a: \"Ce sont les points sensibles, traités spécifiquement pendant la préparation, car c'est là que la corrosion démarre le plus souvent.\" },\n342\t      { q: \"Et l'hiver, je vais consommer plus de chauffage ?\", a: \"L'effet est minime. En hiver le soleil est bas et peu présent. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n343\t    ],\n344\t    finalCta: {\n345\t      title: 'Protégez votre bac acier de la chaleur et de la rouille.',\n346\t      text: \"CovaMetal 20 traite la corrosion et réduit la chaleur transmise au bâtiment, sans déposer la couverture.\",\n347\t      reassurances: ['Étude sous 48h', 'Traite corrosion et chaleur', 'Sans engagement'],\n348\t    },\n349\t    seo: {\n350\t      intro: 'Le bac acier équipe une grande partie des toitures et bardages industriels, logistiques et commerciaux. Léger et économique, il a deux faiblesses bien connues : il conduit la chaleur et il rouille.',\n351\t      sections: [\n352\t        { title: 'Pourquoi une toiture en bac acier surchauffe et se corrode', text: \"Le métal est un excellent conducteur thermique. Sous le soleil, une tôle sombre monte très vite en température et transmet cette chaleur à l'intérieur presque sans inertie. Le cycle chaleur-condensation accélère ensuite la corrosion.\" },\n353\t        { title: 'Le cool roof sur bac acier, comment ça marche', text: 'Covalba applique CovaMetal 20, une peinture anticorrosion réfléchissante qui protège le métal tout en renvoyant le rayonnement solaire, puis une finition CovaTherm pour une réflectivité durable.' },\n354\t        { title: 'Peinture réfléchissante bac acier ou remplacement de couverture', text: \"Remplacer une couverture en bac acier est une opération lourde. Une peinture cool roof bac acier coûte une fraction de ce prix, se pose sans arrêt d'activité et prolonge la couverture existante.\" },\n355\t        { title: 'Quand la tôle est trop corrodée', text: 'Le cool roof protège un métal sain ou en début de corrosion, il ne sauve pas une tôle perforée. Les tôles concernées doivent être remplacées avant traitement.' },\n356\t        { title: 'Un principe validé par la recherche', text: \"L'efficacité des surfaces réfléchissantes est documentée par les laboratoires publics. Sur une grande toiture en bac acier, fortement exposée, ces effets se traduisent directement en confort et en économies.\" },\n357\t        { title: 'Une protection qui dure', text: \"Sur du métal, la tenue du revêtement est déterminante. Le système CovaMetal 20 et sa finition CovaTherm sont conçus pour protéger et réfléchir 8 à 10 ans.\" },\n358\t      ],\n359\t      sources: commonSources,\n360\t    },\n361\t    productName: 'CovaMetal 20',\n362\t  },\n363\t  {\n364\t    slug: 'toiture-plate',\n365\t    path: '/toitures/toiture-plate',\n366\t    title: 'Cool roof toiture plate et terrasse : -10°C, étanchéité',\n367\t    description: 'Cool roof pour toiture plate et terrasse : -8 à -10°C sous toiture et étanchéité renforcée avec CovaSeal 20, pour 20-25 €/m².',\n368\t    canonical: 'https://www.covalba.fr/toitures/toiture-plate',\n369\t    eyebrow: 'COOL ROOF TOITURE PLATE',\n370\t    breadcrumb: 'Toiture plate et terrasse',\n371\t    h1: 'Cool roof pour toiture plate et terrasse',\n372\t    lead: 'Rafraîchissez votre toiture-terrasse de 8 à 10°C et sécurisez son étanchéité, sans tout refaire.',\n373\t    heroImage: covaSealHero,\n374\t    heroImageAlt: 'Application de CovaSeal 20 sur une toiture plate et terrasse',\n375\t    heroStats: [\n376\t      { value: '-8 à -10°C', label: 'sous toiture' },\n377\t      { value: 'Étanchéité', label: '+ fraîcheur en une opération' },\n378\t      { value: '20-25 €/m²', label: 'contre 80-150 €' },\n379\t    ],\n380\t    reassurance: ['Pose sécurisée, machines au pied', 'Compatible terrasses étanchées', 'Étanchéité liquide réfléchissante', \"Sans interrompre l'activité\"],\n381\t    problem: {\n382\t      badge: 'Pourquoi agir',\n383\t      title: 'À plat, votre toiture prend le soleil toute la journée.',\n384\t      cards: [\n385\t        { title: 'Une exposition maximale', text: \"Une toiture plate reçoit le rayonnement solaire de plein fouet du matin au soir. C'est la surface la plus chaude du bâtiment.\" },\n386\t        { title: 'Une étanchéité sous tension', text: \"Chaleur, UV et stagnation d'eau fatiguent l'étanchéité d'une terrasse. Les points singuliers sont les premiers à lâcher.\" },\n387\t        { title: 'Un confort qui se dégrade', text: 'Sous une terrasse surchauffée, les derniers niveaux deviennent difficiles à vivre l’été, et la climatisation s’emballe.' },\n388\t      ],\n389\t      transition: 'Une seule solution peut rafraîchir la terrasse et renforcer son étanchéité.',\n390\t    },\n391\t    solution: {\n392\t      badge: 'La solution Covalba',\n393\t      title: 'Votre terrasse renvoie le soleil et reste étanche.',\n394\t      text: \"Sur une toiture-terrasse, Covalba applique CovaSeal 20, une étanchéité liquide réfléchissante : elle renforce l'étanchéité existante tout en renvoyant le rayonnement solaire. Si le support est déjà parfaitement étanche, le système CovaTherm suffit à rafraîchir.\",\n395\t      systemTitle: 'Le système',\n396\t      system: [\"Étude de l'étanchéité et des points singuliers\", 'CovaSeal 20 ou CovaTherm selon l’état', 'Traitement soigné des relevés, évacuations et joints'],\n397\t      cta: { label: 'Voir la fiche CovaSeal 20', href: '/solutions/covaseal-20' },\n398\t    },\n399\t    benefits: {\n400\t      badge: 'Ce que vous y gagnez',\n401\t      title: 'Fraîche dessus, étanche dessous.',\n402\t      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n403\t      cards: [\n404\t        { title: 'Des niveaux supérieurs tempérés', text: 'La terrasse ne renvoie plus sa chaleur vers le bas. Les espaces sous toiture redeviennent confortables.' },\n405\t        { title: 'Une étanchéité renforcée', text: \"CovaSeal 20 conforte l'étanchéité existante et sécurise les points sensibles.\" },\n406\t        { title: 'Une réfection repoussée', text: 'Vous décalez de plusieurs années une réfection lourde et coûteuse.' },\n407\t        { title: 'Une facture allégée', text: 'Sur les bâtiments climatisés, les besoins de froid baissent.' },\n408\t      ],\n409\t    },\n410\t    roi: {\n411\t      badge: 'Le bon calcul',\n412\t      title: 'Sécuriser la terrasse avant la réfection lourde.',\n413\t      intro: \"Tant que la structure reste saine, l'étanchéité liquide réfléchissante renforce et rafraîchit sans refaire toute la terrasse.\",\n414\t      comparison: defaultRoiComparison,\n415\t      afterComparison: \"Vous gagnez en confort d'été tout en repoussant une intervention beaucoup plus lourde.\",\n416\t      situation: {\n417\t        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n418\t        rows: [\n419\t          ['Étanchéité saine exposée à la chaleur', 'Cool roof CovaTherm', \"On rafraîchit et on protège l'étanchéité, investissement minimal.\"],\n420\t          ['Étanchéité vieillissante sans fuite', 'CovaSeal 20', \"Renforce l'étanchéité et rafraîchit pour environ un quart du prix d'une réfection.\"],\n421\t          ['Fuites actives', 'Reprise des points singuliers puis CovaSeal 20, ou réfection si trop dégradée', 'On sécurise d’abord, on ne couvre pas une fuite.'],\n422\t          ['Isolation insuffisante', \"Cool roof pour l'été, à comparer avec une réfection complète si l'isolation est le sujet\", \"Le cool roof agit sur l'été, pas sur l'isolation.\"],\n423\t        ],\n424\t      },\n425\t      cta: 'Demander un devis',\n426\t    },\n427\t    midCta: {\n428\t      title: 'Rafraîchissez votre terrasse tout en sécurisant l’étanchéité.',\n429\t      text: \"On vérifie les relevés, évacuations et zones de stagnation pour choisir entre CovaSeal 20, CovaTherm ou une reprise plus lourde.\",\n430\t    },\n431\t    compatibility: {\n432\t      badge: 'Sur quoi on intervient',\n433\t      title: 'Compatible avec la plupart des terrasses étanchées.',\n434\t      text: \"Le système s'applique sur les toitures-terrasses en membrane bitumineuse, en membrane synthétique ou sur support béton étanché. Tout commence par une étude de l'étanchéité, des relevés, des évacuations et des zones de stagnation.\",\n435\t      image: covaSealHero,\n436\t    },\n437\t    method: {\n438\t      badge: 'Chantier en sécurité',\n439\t      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n440\t      steps: [\n441\t        { title: 'Étude technique', text: \"État de l'étanchéité, points singuliers, stagnation, objectifs thermiques.\" },\n442\t        { title: 'Préparation', text: 'Nettoyage et reprise des points sensibles.' },\n443\t        { title: 'Application sécurisée', text: \"CovaSeal 20 ou CovaTherm, projection depuis l'extérieur.\" },\n444\t        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n445\t      ],\n446\t      reassurance: \"Machines au pied du bâtiment, rien sur le toit. Aucun risque pour la structure ni pour l'étanchéité, applicateurs formés et équipés.\",\n447\t    },\n448\t    proof: {\n449\t      badge: 'Des résultats concrets',\n450\t      title: 'Mesuré sur le terrain.',\n451\t      figures: [\n452\t        { value: '-7,5°C', label: 'sur la température de surface' },\n453\t        { value: 'Étanchéité renforcée', label: 'sur les zones traitées' },\n454\t        { value: '+8 à 10 ans', label: \"d'étanchéité préservée\" },\n455\t      ],\n456\t    },\n457\t    faq: [\n458\t      { q: 'Quelle différence entre CovaTherm et CovaSeal 20 sur ma terrasse ?', a: \"CovaTherm rafraîchit une étanchéité encore saine. CovaSeal 20 renforce l'étanchéité tout en rafraîchissant.\" },\n459\t      { q: \"Ça gère la stagnation d'eau ?\", a: \"L'étude technique identifie les zones de stagnation et les points singuliers, qui sont repris avant application.\" },\n460\t      { q: \"Combien de temps ça prolonge mon étanchéité ?\", a: 'En la protégeant des UV et de la chaleur, vous gagnez en général 8 à 10 ans avant une réfection.' },\n461\t      { q: \"Faut-il déposer l'ancienne étanchéité ?\", a: 'Non. Un nettoyage et la reprise des points singuliers suffisent.' },\n462\t      { q: \"Et l'hiver ?\", a: \"L'effet est minime. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n463\t    ],\n464\t    finalCta: {\n465\t      title: 'Gagnez en fraîcheur sans refaire toute la terrasse.',\n466\t      text: \"CovaSeal 20 renforce l'étanchéité existante et réduit la chaleur sous toiture, avec un chantier extérieur sans interruption d'activité.\",\n467\t      reassurances: ['Étude sous 48h', 'Étanchéité et fraîcheur', 'Sans engagement'],\n468\t    },\n469\t    seo: {\n470\t      intro: \"La toiture plate, ou toiture-terrasse, est la configuration la plus exposée au soleil. Elle concentre deux enjeux : la surchauffe du bâtiment et la durabilité de l'étanchéité.\",\n471\t      sections: [\n472\t        { title: 'Pourquoi une toiture plate surchauffe', text: \"Une toiture-terrasse reçoit le soleil de face une grande partie de la journée. La surface, souvent sombre, peut dépasser 80°C en été, et cette chaleur descend directement vers les niveaux situés en dessous.\" },\n473\t        { title: 'Le cool roof sur toiture plate, comment ça marche', text: \"La peinture réfléchissante renvoie le rayonnement au lieu de l'absorber. Sur une terrasse, Covalba privilégie souvent CovaSeal 20 pour renforcer l'étanchéité et rafraîchir en une seule opération.\" },\n474\t        { title: \"Cool roof toiture terrasse ou réfection d'étanchéité\", text: \"Refaire l'étanchéité d'une terrasse coûte de 80 à 100 €/m², et jusqu'à 150 €/m² avec isolation. Une solution cool roof se situe autour de 20 à 25 €/m².\" },\n475\t        { title: \"Quand choisir l'étanchéité liquide réfléchissante\", text: \"Une étanchéité encore en place mais fatiguée, sans fuite déclarée, est la situation idéale pour CovaSeal 20.\" },\n476\t        { title: 'Un principe validé par la recherche', text: \"Sur une terrasse, surface la plus exposée du bâtiment, l'effet des toitures réfléchissantes est au maximum de son potentiel.\" },\n477\t        { title: 'Une protection qui dure', text: 'Les systèmes Covalba, CovaSeal 20 et CovaTherm, sont conçus pour tenir 8 à 10 ans.' },\n478\t      ],\n479\t      sources: commonSources,\n480\t    },\n481\t    productName: 'CovaSeal 20',\n482\t  },\n483\t  {\n484\t    slug: 'fibrociment',\n485\t    path: '/toitures/fibrociment',\n486\t    title: 'Cool roof fibrociment : -10°C sans dépose | Covalba',\n487\t    description: 'Cool roof pour toiture fibrociment : -8 à -10°C sous toiture sans dépose, pour 20-25 €/m². Alternative à un chantier de dépose coûteux.',\n488\t    canonical: 'https://www.covalba.fr/toitures/fibrociment',\n489\t    eyebrow: 'COOL ROOF FIBROCIMENT',\n490\t    breadcrumb: 'Fibrociment',\n491\t    h1: 'Cool roof pour toiture en fibrociment',\n492\t    lead: 'Rafraîchissez votre toiture fibrociment de 8 à 10°C sans la déposer, et évitez un chantier de dépose coûteux.',\n493\t    heroImage: compatFibrociment,\n494\t    heroImageAlt: 'Toiture fibrociment ancienne traitable avec préparation douce',\n495\t    heroStats: [\n496\t      { value: '-8 à -10°C', label: 'sous toiture' },\n497\t      { value: 'Sans dépose', label: 'de la couverture' },\n498\t      { value: '20-25 €/m²', label: 'sans gros chantier' },\n499\t    ],\n500\t    reassurance: ['Pose sécurisée, machines au pied', 'Application sans agression du support', 'Sans dépose', \"Sans interrompre l'activité\"],\n501\t    problem: {\n502\t      badge: 'Pourquoi agir',\n503\t      title: 'Le fibrociment chauffe, vieillit, et coûte cher à déposer.',\n504\t      cards: [\n505\t        { title: 'Il absorbe et transmet la chaleur', text: \"Les plaques de fibrociment, souvent sombres et peu isolantes, chauffent fort au soleil.\" },\n506\t        { title: 'Il se dégrade avec le temps', text: 'Le fibrociment devient poreux, se couvre de mousses et perd son aspect.' },\n507\t        { title: 'Le déposer coûte très cher', text: 'Sur les couvertures anciennes, la dépose est un chantier lourd et coûteux, surtout si le matériau relève de la réglementation amiante.' },\n508\t      ],\n509\t      transition: 'Le cool roof permet de rafraîchir et protéger sans toucher à la couverture.',\n510\t    },\n511\t    solution: {\n512\t      badge: 'La solution Covalba',\n513\t      title: 'Votre toiture renvoie le soleil, sans dépose.',\n514\t      text: 'Sur une toiture fibrociment, Covalba applique le système CovaTherm avec une préparation douce, adaptée à la fragilité du support. Le revêtement réfléchissant renvoie le rayonnement solaire, fait baisser la température et protège les plaques, sans dépose ni perçage.',\n515\t      systemTitle: 'Le système',\n516\t      system: [\"Étude de l'état des plaques et préparation adaptée\", 'Revêtement réfléchissant CovaTherm', 'Laque solaire sur les lanterneaux si besoin'],\n517\t      note: \"Sur fibrociment susceptible de contenir de l'amiante, l'application relève de la réglementation en vigueur. Le cadre est précisé lors de l'étude technique.\",\n518\t      cta: { label: 'Voir la fiche CovaTherm', href: '/solutions/covatherm' },\n519\t    },\n520\t    benefits: {\n521\t      badge: 'Ce que vous y gagnez',\n522\t      title: 'Plus frais, protégé, sans gros chantier.',\n523\t      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n524\t      cards: [\n525\t        { title: 'Un bâtiment tempéré', text: 'Le fibrociment ne transmet plus sa chaleur. Hangars et ateliers redeviennent supportables l’été.' },\n526\t        { title: 'Une couverture protégée', text: 'Le revêtement protège les plaques des UV et limite leur vieillissement.' },\n527\t        { title: 'Pas de dépose', text: 'Vous évitez un chantier lourd et le surcoût associé aux matériaux anciens.' },\n528\t        { title: 'Une intervention rapide', text: \"Application depuis l'extérieur, sans interrompre l'activité du site.\" },\n529\t      ],\n530\t    },\n531\t    roi: {\n532\t      badge: 'Le bon calcul',\n533\t      title: 'Protéger les plaques plutôt que déposer.',\n534\t      intro: 'Tant que les plaques sont saines, le cool roof réduit la chaleur et évite un chantier de dépose lourd.',\n535\t      afterComparison: \"Sur fibrociment, l'écart de coût avec la dépose est souvent le plus marqué.\",\n536\t      situation: {\n537\t        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n538\t        rows: [\n539\t          ['Plaques saines exposées à la chaleur', 'Cool roof CovaTherm, préparation douce', 'On rafraîchit et on protège sans toucher à la couverture.'],\n540\t          ['Plaques poreuses, mousses', 'Nettoyage doux adapté puis CovaTherm', 'On régénère l’aspect et on protège, sans dépose.'],\n541\t          ['Plaques cassées ou fissurées', \"Remplacement ponctuel des plaques, puis traitement de l'ensemble\", 'On reprend le ponctuel avant de protéger le reste.'],\n542\t          ['Couverture en fin de vie', 'Étude technique : la dépose peut s’imposer', 'Le cool roof ne sauve pas une couverture condamnée.'],\n543\t        ],\n544\t      },\n545\t      cta: 'Demander un devis',\n546\t    },\n547\t    midCta: {\n548\t      title: 'Rafraîchissez votre fibrociment sans dépose.',\n549\t      text: \"On vérifie l'état des plaques et le cadre réglementaire pour traiter la chaleur sans engager un chantier lourd de dépose.\",\n550\t    },\n551\t    compatibility: {\n552\t      badge: 'Sur quoi on intervient',\n553\t      title: 'Une préparation douce, adaptée à un support fragile.',\n554\t      text: 'Le fibrociment est un support fragile qui ne supporte pas les traitements agressifs. La préparation est donc adaptée : nettoyage doux, sans projection à haute pression susceptible de dégrader les plaques, et sans perçage.',\n555\t      image: compatFibrociment,\n556\t    },\n557\t    method: {\n558\t      badge: 'Chantier en sécurité',\n559\t      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n560\t      steps: [\n561\t        { title: 'Étude technique', text: 'État des plaques, mousses, solidité, contexte réglementaire.' },\n562\t        { title: 'Préparation', text: 'Nettoyage doux adapté, sans agresser le support.' },\n563\t        { title: 'Application sécurisée', text: \"Revêtement réfléchissant, projection depuis l'extérieur.\" },\n564\t        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n565\t      ],\n566\t      reassurance: 'Machines au pied du bâtiment, rien sur le toit. Application sans agression du support, applicateurs formés et équipés.',\n567\t    },\n568\t    proof: {\n569\t      badge: 'Des résultats concrets',\n570\t      title: 'Mesuré sur le terrain.',\n571\t      figures: [\n572\t        { value: '-7,5°C', label: 'sur la température de surface' },\n573\t        { value: 'Plaques protégées', label: 'des UV et du vieillissement' },\n574\t        { value: 'Sans dépose', label: 'de la couverture' },\n575\t      ],\n576\t    },\n577\t    faq: [\n578\t      { q: \"Et si mon fibrociment contient de l'amiante ?\", a: \"L'application d'un revêtement sur fibrociment amianté relève de la réglementation sur l'amiante. L'étude technique précise le cadre applicable à votre toiture.\" },\n579\t      { q: 'Ça tient sur un fibrociment poreux ?', a: 'Oui, après une préparation adaptée. Un fibrociment poreux ou couvert de mousses est nettoyé en douceur avant application.' },\n580\t      { q: 'Faut-il déposer les plaques ?', a: \"Non, c'est tout l'intérêt. Seules les plaques cassées sont remplacées ponctuellement.\" },\n581\t      { q: 'Pourquoi ne pas nettoyer à haute pression ?', a: \"Parce que le fibrociment est fragile et qu'un nettoyage agressif le dégrade.\" },\n582\t      { q: \"Et l'hiver ?\", a: \"L'effet est minime. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n583\t    ],\n584\t    finalCta: {\n585\t      title: 'Évitez la dépose quand les plaques peuvent être protégées.',\n586\t      text: \"CovaTherm rafraîchit et protège le fibrociment avec une préparation douce, adaptée au support et à son cadre réglementaire.\",\n587\t      reassurances: ['Étude sous 48h', 'Sans dépose', 'Sans engagement'],\n588\t    },\n589\t    seo: {\n590\t      intro: 'Le fibrociment couvre une grande partie des hangars industriels et agricoles construits au siècle dernier. Il chauffe fort, vieillit mal, et sa dépose est devenue coûteuse.',\n591\t      sections: [\n592\t        { title: 'Pourquoi une toiture en fibrociment surchauffe', text: \"Les plaques de fibrociment, souvent sombres et faiblement isolantes, absorbent le rayonnement solaire et le transmettent à l'intérieur.\" },\n593\t        { title: 'Le cool roof sur fibrociment, comment ça marche', text: 'CovaTherm est appliqué avec une préparation douce : pas de nettoyage agressif, pas de perçage. Le revêtement rafraîchit et protège les plaques du vieillissement.' },\n594\t        { title: \"Le cas de l'amiante\", text: \"Une grande partie des toitures fibrociment anciennes contiennent de l'amiante. Dans ce cas, l'intervention doit suivre la réglementation applicable, précisée toiture par toiture.\" },\n595\t        { title: 'Cool roof fibrociment ou dépose', text: \"Déposer une couverture ancienne, l'évacuer puis poser une nouvelle toiture représente un budget très élevé. Tant que les plaques sont saines, le cool roof est l'option la plus rentable.\" },\n596\t        { title: 'Un principe validé par la recherche', text: \"Sur un hangar fibrociment mal isolé, l'effet des surfaces réfléchissantes se ressent rapidement sur le confort d'été.\" },\n597\t        { title: 'Une protection qui dure', text: \"Le système CovaTherm, en polyuréthane avec vernis de finition, est conçu pour tenir 8 à 10 ans tout en protégeant les plaques.\" },\n598\t      ],\n599\t      sources: commonSources,\n600\t    },\n601\t    productName: 'CovaTherm',\n602\t  },\n603\t  {\n604\t    slug: 'tuiles-ciment',\n605\t    path: '/toitures/tuiles-ciment',\n606\t    title: 'Cool roof tuiles ciment : -10°C dans les combles | Covalba',\n607\t    description: 'Cool roof pour toiture en tuiles ciment : -8 à -10°C sous toiture, des combles plus frais sans dépose, pour 20-25 €/m².',\n608\t    canonical: 'https://www.covalba.fr/toitures/tuiles-ciment',\n609\t    eyebrow: 'COOL ROOF TUILES CIMENT',\n610\t    breadcrumb: 'Tuiles ciment',\n611\t    h1: 'Cool roof pour toiture en tuiles ciment',\n612\t    lead: 'Rafraîchissez vos combles de 8 à 10°C en traitant la tuile, sans rénovation lourde.',\n613\t    heroImage: compatTuile,\n614\t    heroImageAlt: 'Toiture en tuiles ciment compatible avec revêtement réfléchissant',\n615\t    heroStats: [\n616\t      { value: '-8 à -10°C', label: 'sous toiture' },\n617\t      { value: 'Sans dépose', label: 'des tuiles' },\n618\t      { value: '20-25 €/m²', label: 'sans gros chantier' },\n619\t    ],\n620\t    reassurance: ['Pose sécurisée, machines au pied', 'Adapté aux supports poreux', 'Sans dépose', \"Sans interrompre l'usage du bâtiment\"],\n621\t    problem: {\n622\t      badge: 'Pourquoi agir',\n623\t      title: 'Des tuiles ciment foncées, des combles en surchauffe.',\n624\t      cards: [\n625\t        { title: 'Elles emmagasinent la chaleur', text: 'Les tuiles ciment accumulent la chaleur du soleil et la diffusent vers les combles et les niveaux sous toiture.' },\n626\t        { title: 'Elles deviennent poreuses', text: 'Avec les années, la tuile ciment se patine, devient poreuse et se couvre de mousses.' },\n627\t        { title: 'Les combles surchauffent', text: 'Sous une toiture exposée, les combles et les derniers étages deviennent difficiles à occuper l’été.' },\n628\t      ],\n629\t      transition: 'Le cool roof traite la tuile pour rafraîchir, sans rénovation lourde.',\n630\t    },\n631\t    solution: {\n632\t      badge: 'La solution Covalba',\n633\t      title: 'Vos tuiles renvoient le soleil au lieu de le stocker.',\n634\t      text: 'Sur une toiture en tuiles ciment, Covalba applique le système CovaTherm, adapté aux supports poreux. Le revêtement réfléchissant renvoie le rayonnement solaire, fait baisser la température des combles et protège la tuile, sans dépose.',\n635\t      systemTitle: 'Le système',\n636\t      system: ['Nettoyage et traitement des mousses', 'Revêtement réfléchissant CovaTherm', 'Laque solaire sur les ouvertures vitrées si besoin'],\n637\t      cta: { label: 'Voir la fiche CovaTherm', href: '/solutions/covatherm' },\n638\t    },\n639\t    benefits: {\n640\t      badge: 'Ce que vous y gagnez',\n641\t      title: 'Des combles tempérés, une toiture protégée.',\n642\t      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n643\t      cards: [\n644\t        { title: 'Des combles plus frais', text: 'La tuile ne diffuse plus sa chaleur. Les espaces sous toiture redeviennent confortables l’été.' },\n645\t        { title: 'Une tuile protégée', text: 'Le revêtement protège la tuile de la porosité, des mousses et des UV.' },\n646\t        { title: 'Un aspect rénové', text: 'La toiture retrouve un aspect propre et uniforme, sans remplacement des tuiles.' },\n647\t        { title: 'Sans gros chantier', text: 'Application depuis l’extérieur, sans dépose ni rénovation lourde.' },\n648\t      ],\n649\t    },\n650\t    roi: {\n651\t      badge: 'Le bon calcul',\n652\t      title: 'Rafraîchir les combles sans refaire la toiture.',\n653\t      intro: 'Tant que les tuiles sont saines, le cool roof limite la chaleur et protège la couverture sans rénovation lourde.',\n654\t      situation: {\n655\t        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n656\t        rows: [\n657\t          ['Tuiles saines, combles chauds', 'Cool roof CovaTherm', 'On rafraîchit et on protège la tuile, investissement léger.'],\n658\t          ['Tuiles poreuses, mousses', 'Nettoyage et traitement puis CovaTherm', 'On régénère l’aspect et on protège, sans dépose.'],\n659\t          ['Tuiles cassées', \"Remplacement ponctuel puis traitement de l'ensemble\", 'On reprend le ponctuel avant de protéger le reste.'],\n660\t          ['Combles à rénover en profondeur', 'Comparer avec une isolation si le confort hiver est aussi en jeu', \"Le cool roof agit sur l'été, pas sur l'isolation.\"],\n661\t        ],\n662\t      },\n663\t      cta: 'Demander un devis',\n664\t    },\n665\t    midCta: {\n666\t      title: 'Rafraîchissez vos combles sans changer les tuiles.',\n667\t      text: \"On vérifie la porosité, les mousses et les tuiles cassées pour chiffrer un traitement qui protège la couverture et limite la chaleur.\",\n668\t    },\n669\t    compatibility: {\n670\t      badge: 'Sur quoi on intervient',\n671\t      title: 'Adapté à la tuile ciment, un support poreux.',\n672\t      text: \"La tuile ciment est un support poreux qui demande une préparation soignée. On évalue l'état de la couverture, on traite les mousses et les lichens, on nettoie, puis on applique le revêtement réfléchissant adapté.\",\n673\t      image: compatTuile,\n674\t    },\n675\t    method: {\n676\t      badge: 'Chantier en sécurité',\n677\t      title: \"Tout se passe depuis l'extérieur, en sécurité.\",\n678\t      steps: [\n679\t        { title: 'Étude technique', text: 'État des tuiles, mousses, porosité, objectifs thermiques.' },\n680\t        { title: 'Préparation', text: 'Nettoyage, traitement des mousses, remplacement ponctuel si besoin.' },\n681\t        { title: 'Application sécurisée', text: 'Revêtement réfléchissant adapté au support.' },\n682\t        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n683\t      ],\n684\t      reassurance: 'Machines au pied du bâtiment, applicateurs formés et équipés, travail en sécurité.',\n685\t    },\n686\t    proof: {\n687\t      badge: 'Des résultats concrets',\n688\t      title: 'Mesuré sur le terrain.',\n689\t      figures: [\n690\t        { value: '-7,5°C', label: 'sur la température de surface' },\n691\t        { value: 'Combles rafraîchis', label: 'sous toiture exposée' },\n692\t        { value: 'Sans dépose', label: 'de la couverture' },\n693\t      ],\n694\t    },\n695\t    faq: [\n696\t      { q: 'Ça tient sur une tuile poreuse ?', a: 'Oui, après une préparation adaptée. La tuile est nettoyée et les mousses traitées avant application.' },\n697\t      { q: 'Faut-il remplacer les tuiles ?', a: 'Non, sauf les tuiles cassées, reprises ponctuellement.' },\n698\t      { q: 'Le cool roof isole-t-il mes combles ?', a: \"Il agit sur la chaleur d'été. Il ne remplace pas une isolation, qui joue sur le confort d'hiver.\" },\n699\t      { q: \"Et l'hiver ?\", a: \"L'effet est minime. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n700\t    ],\n701\t    finalCta: {\n702\t      title: 'Des combles plus frais sans rénovation lourde.',\n703\t      text: \"CovaTherm réfléchit le soleil, protège les tuiles ciment et améliore le confort d'été sans déposer la couverture.\",\n704\t      reassurances: ['Étude sous 48h', 'Sans dépose', 'Sans engagement'],\n705\t    },\n706\t    seo: {\n707\t      intro: 'La tuile ciment couvre de nombreuses toitures en pente du parc tertiaire, des établissements recevant du public et du résidentiel collectif. Elle emmagasine la chaleur et se patine avec le temps.',\n708\t      sections: [\n709\t        { title: 'Pourquoi une toiture en tuiles ciment surchauffe', text: 'La tuile ciment est un matériau dense qui accumule la chaleur du soleil au fil de la journée et la restitue lentement vers les combles.' },\n710\t        { title: 'Le cool roof sur tuiles ciment, comment ça marche', text: 'La peinture réfléchissante renvoie le rayonnement solaire plutôt que de le stocker. CovaTherm adhère aux supports poreux après nettoyage et traitement des mousses.' },\n711\t        { title: 'Cool roof tuiles ciment ou rénovation de toiture', text: \"Tant que les tuiles sont saines, un cool roof à 20-25 €/m² protège la couverture et rafraîchit les combles pour une fraction du prix.\" },\n712\t        { title: 'Un principe validé par la recherche', text: \"Sur des combles sous tuiles exposées, le gain de confort d'une surface réfléchissante est rapidement ressenti.\" },\n713\t        { title: 'Une protection qui dure', text: 'Le système CovaTherm est conçu pour tenir 8 à 10 ans tout en protégeant la tuile.' },\n714\t      ],\n715\t      sources: commonSources,\n716\t    },\n717\t    productName: 'CovaTherm',\n718\t  },\n719\t];\n720\t\n721\texport const roofPageBySlug = Object.fromEntries(roofPages.map((page) => [page.slug, page])) as Record<string, RoofPageData>;\n722\t"}]},"uuid":"f6063b22-3869-4339-81b7-e4a645be13bc","timestamp":"2026-06-22T00:03:23.978Z","toolUseResult":{"type":"text","file":{"filePath":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/data/roofPages.ts","content":"import covaSealHero from '@/assets/produits/covaseal-hero.webp';\nimport compatBacAcier from '@/assets/produits/compat-bac-acier.webp';\nimport compatBitume from '@/assets/produits/compat-bitume.webp';\nimport compatFibrociment from '@/assets/produits/compat-fibrociment.webp';\nimport compatTuile from '@/assets/produits/compat-tuile.webp';\n\nexport interface TextCard {\n  title: string;\n  text: string;\n}\n\nexport interface TableBlock {\n  headers: string[];\n  rows: string[][];\n}\n\nexport interface RoofPageData {\n  slug: string;\n  path: string;\n  title: string;\n  description: string;\n  canonical: string;\n  eyebrow: string;\n  breadcrumb: string;\n  h1: string;\n  lead: string;\n  heroImage: string;\n  heroImageAlt: string;\n  heroStats: { value: string; label: string }[];\n  reassurance: string[];\n  problem: {\n    badge: string;\n    title: string;\n    cards: TextCard[];\n    transition: string;\n  };\n  solution: {\n    badge: string;\n    title: string;\n    text: string;\n    systemTitle: string;\n    system: string[];\n    note?: string;\n    cta: { label: string; href: string };\n  };\n  benefits: {\n    badge: string;\n    title: string;\n    featured: string;\n    cards: TextCard[];\n  };\n  roi: {\n    badge: string;\n    title: string;\n    intro: string;\n    comparison?: TableBlock;\n    afterComparison?: string;\n    situation: TableBlock;\n    cta: string;\n  };\n  midCta: {\n    title: string;\n    text: string;\n  };\n  compatibility: {\n    badge: string;\n    title: string;\n    text: string;\n    image: string;\n  };\n  method: {\n    badge: string;\n    title: string;\n    steps: TextCard[];\n    reassurance: string;\n  };\n  proof: {\n    badge: string;\n    title: string;\n    figures: { value: string; label: string }[];\n  };\n  faq: { q: string; a: string }[];\n  finalCta: {\n    title: string;\n    text: string;\n    reassurances: string[];\n  };\n  seo: {\n    intro: string;\n    sections: TextCard[];\n    sources: { label: string; href: string }[];\n  };\n  productName: string;\n}\n\nconst commonSources = [\n  { label: 'Lawrence Berkeley National Laboratory, Heat Island Group, Cool Roofs', href: 'https://heatisland.lbl.gov/coolscience/cool-roofs' },\n  { label: 'U.S. Environmental Protection Agency, Using Cool Roofs to Reduce Heat Islands', href: 'https://www.epa.gov/heatislands/using-cool-roofs-reduce-heat-islands' },\n  { label: 'EPA, Reducing Urban Heat Islands: Compendium of Strategies, Cool Roofs', href: 'https://www.epa.gov/sites/default/files/2014-06/documents/coolroofscompendium.pdf' },\n  { label: 'ADEME, ilots de chaleur urbains et rafraichissement passif', href: 'https://www.ademe.fr' },\n];\n\nconst defaultRoiComparison: TableBlock = {\n  headers: ['', 'Cool roof Covalba', 'Membrane neuve blanche', 'Remplacement isolation + etancheite'],\n  rows: [\n    ['Investissement', '~112 500 EUR (22,5 EUR/m2)', '~450 000 EUR (90 EUR/m2)', '~750 000 EUR (150 EUR/m2)'],\n    ['Duree de vie', '8-10 ans', '15-20 ans', '20+ ans'],\n    ['Investissement decale', 'reference', \"jusqu'a +337 500 EUR\", \"jusqu'a +637 500 EUR\"],\n    ['Activite pendant le chantier', 'maintenue', 'interrompue', 'interrompue'],\n  ],\n};\n\nexport const roofPages: RoofPageData[] = [\n  {\n    slug: 'membrane-bitumineuse',\n    path: '/toitures/membrane-bitumineuse',\n    title: 'Cool roof membrane bitumineuse : -10°C et étanchéité prolongée',\n    description: \"Cool roof pour toiture en membrane bitumineuse : -8 à -10°C sous toiture et 8 à 10 ans d'étanchéité gagnés, pour 20-25 €/m².\",\n    canonical: 'https://www.covalba.fr/toitures/membrane-bitumineuse',\n    eyebrow: 'COOL ROOF MEMBRANE BITUMINEUSE',\n    breadcrumb: 'Membrane bitumineuse',\n    h1: 'Cool roof pour toiture en membrane bitumineuse',\n    lead: \"Rafraîchissez votre toiture bitume de 8 à 10°C et prolongez son étanchéité, sans la remplacer.\",\n    heroImage: compatBitume,\n    heroImageAlt: 'Toiture terrasse en membrane bitumineuse compatible avec un traitement cool roof',\n    heroStats: [\n      { value: '-8 à -10°C', label: 'sous toiture' },\n      { value: '+8 à 10 ans', label: \"d'étanchéité\" },\n      { value: '20-25 €/m²', label: 'contre 80-100 €' },\n    ],\n    reassurance: ['Pose sécurisée, machines au pied', 'Compatible bitume et auto-protégé', 'Tenue 8 à 10 ans', 'Sans interrompre votre activité'],\n    problem: {\n      badge: 'Pourquoi agir',\n      title: \"Une toiture bitume noire, c'est un radiateur posé sur votre bâtiment.\",\n      cards: [\n        { title: 'Elle absorbe tout le soleil', text: \"Une membrane bitumineuse sombre peut grimper à 80°C en surface l'été. Cette chaleur traverse la toiture et fait monter la température à l'intérieur.\" },\n        { title: 'Elle vieillit plus vite', text: \"Les UV et les chocs thermiques font farinier puis fissurer le bitume. Plus la membrane chauffe, plus elle se fragilise.\" },\n        { title: 'Elle pèse sur votre confort et votre facture', text: \"Sous une toiture surchauffée, les équipes peinent et la climatisation, là où il y en a, tourne en surrégime.\" },\n      ],\n      transition: 'Bonne nouvelle, on traite la cause sans toucher à la structure.',\n    },\n    solution: {\n      badge: 'La solution Covalba',\n      title: \"Votre toiture renvoie le soleil au lieu de l'absorber.\",\n      text: \"Sur une membrane bitumineuse, Covalba applique le système CovaTherm : un revêtement réfléchissant clair qui renvoie jusqu'à 90 % du rayonnement solaire. La membrane ne chauffe plus, le bâtiment reste tempéré, et l'étanchéité existante est protégée des UV. Si elle est déjà fatiguée, CovaSeal 20 refait l'étanchéité et rafraîchit en une seule opération.\",\n      systemTitle: 'Le système en 3 couches',\n      system: [\"Primaire d'accroche adapté au bitume\", 'Base coat CovaTherm 8 en polyuréthane', 'Top coat CovaTherm 20 pour la tenue dans le temps'],\n      cta: { label: 'Voir la fiche CovaTherm', href: '/solutions/covatherm' },\n    },\n    benefits: {\n      badge: 'Ce que vous y gagnez',\n      title: 'Plus frais dessous, plus durable dessus.',\n      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n      cards: [\n        { title: 'Un bâtiment tempéré', text: 'La chaleur ne passe plus par le toit. Les derniers niveaux redeviennent confortables sans climatiser davantage.' },\n        { title: 'Une étanchéité prolongée', text: 'Protégée des UV et des chocs thermiques, votre membrane gagne 8 à 10 ans de durée de vie.' },\n        { title: 'Une facture allégée', text: 'Sur les bâtiments climatisés, les groupes de froid sont moins sollicités.' },\n        { title: 'Un gros chantier repoussé', text: 'Vous décalez le remplacement de toiture, un investissement bien plus lourd.' },\n      ],\n    },\n    roi: {\n      badge: 'Le bon calcul',\n      title: 'Repousser la réfection, sans immobiliser le bâtiment.',\n      intro: \"Tant que la membrane reste viable, le cool roof traite la chaleur et protège l'étanchéité pour une fraction du prix d'une réfection.\",\n      comparison: defaultRoiComparison,\n      afterComparison: \"La logique est simple : moins de chaleur dès cet été, et une grosse dépense repoussée.\",\n      situation: {\n        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n        rows: [\n          ['Membrane saine exposée à la chaleur', 'Cool roof CovaTherm', \"Investissement minimal, confort immédiat, étanchéité prolongée.\"],\n          ['Membrane vieillissante', 'CovaSeal 20', \"Refait l'étanchéité et rafraîchit pour environ un quart du prix d'une membrane neuve.\"],\n          ['Fuites actives', 'Reprise des points singuliers puis CovaSeal 20, ou membrane neuve si trop dégradée', \"On ne peint pas sur une étanchéité morte. On vous le dit franchement.\"],\n          ['Isolation insuffisante', \"Cool roof pour l'été, à comparer avec une réfection complète si l'isolation est le vrai sujet\", \"Le cool roof agit sur l'été, il ne remplace pas l'isolation.\"],\n        ],\n      },\n      cta: 'Demander un devis',\n    },\n    midCta: {\n      title: 'Gagnez de la fraîcheur sans refaire votre toiture bitume.',\n      text: \"On vérifie l'état de la membrane, on chiffre le gain thermique possible et on vous dit si CovaTherm suffit ou si l'étanchéité doit être reprise.\",\n    },\n    compatibility: {\n      badge: 'Sur quoi on intervient',\n      title: 'Compatible avec votre membrane, après étude technique.',\n      text: \"Le système s'applique sur la plupart des membranes bitumineuses : bitume nu, membrane auto-protégée à paillettes ardoisées, multicouche. Tout commence par une étude de l'état de l'étanchéité et des points singuliers. La toiture est nettoyée, les points singuliers sont repris si besoin, puis le primaire garantit l'accroche avant l'application.\",\n      image: compatBitume,\n    },\n    method: {\n      badge: 'Chantier en sécurité',\n      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n      steps: [\n        { title: 'Étude technique', text: 'État de la membrane, points singuliers, objectifs thermiques.' },\n        { title: 'Préparation', text: 'Nettoyage haute pression et reprise des points sensibles.' },\n        { title: 'Application sécurisée', text: \"Projection depuis l'extérieur, seuls les flexibles montent en toiture.\" },\n        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n      ],\n      reassurance: \"Machines au pied du bâtiment, rien sur le toit. Aucun risque pour la structure ni pour l'étanchéité, applicateurs formés et équipés.\",\n    },\n    proof: {\n      badge: 'Des résultats concrets',\n      title: 'Mesuré sur le terrain.',\n      figures: [\n        { value: '-7,5°C', label: 'sur la température de surface' },\n        { value: '+8 à 10 ans', label: \"d'étanchéité préservée\" },\n        { value: '80 %', label: 'de nos chantiers réalisés sur membrane bitumineuse' },\n      ],\n    },\n    faq: [\n      { q: 'Mon étanchéité est vieille, dois-je la refaire avant ?', a: \"Pas forcément. Si la membrane est saine, le cool roof la protège et la prolonge. Si elle farine ou présente des microfissures sans fuite, CovaSeal 20 refait l'étanchéité et rafraîchit en une seule opération. L'étude technique tranche.\" },\n      { q: 'Ça tient sur une membrane à paillettes ardoisées ?', a: \"Oui, après préparation adaptée et primaire d'accroche. C'est l'un des supports les plus courants que nous traitons.\" },\n      { q: \"Combien de temps ça prolonge mon étanchéité ?\", a: \"En la protégeant des UV et des chocs thermiques, le cool roof allonge sa durée de vie de 8 à 10 ans en général.\" },\n      { q: \"Faut-il décaper l'ancienne membrane ?\", a: \"Non. Un nettoyage haute pression suffit, suivi de la reprise des points singuliers si nécessaire.\" },\n      { q: 'Et les fuites existantes ?', a: \"Elles se traitent avant toute application. Le cool roof n'est pas une réparation d'étanchéité, c'est une protection.\" },\n      { q: \"Et l'hiver, je vais consommer plus de chauffage ?\", a: \"L'effet est minime. En hiver le soleil est bas et peu présent. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n    ],\n    finalCta: {\n      title: 'Rafraîchissez votre toiture bitume sans la refaire.',\n      text: \"CovaTherm protège votre membrane, réduit la chaleur sous toiture et repousse une réfection lourde tant que l'étanchéité est viable.\",\n      reassurances: ['Étude sous 48h', '80 % de nos chantiers sur bitume', 'Sans engagement'],\n    },\n    seo: {\n      intro: \"La membrane bitumineuse est le revêtement d'étanchéité le plus répandu sur les toitures-terrasses des bâtiments industriels et tertiaires. Robuste et éprouvée, elle a un défaut : sa couleur sombre en fait un capteur de chaleur.\",\n      sections: [\n        { title: 'Pourquoi une toiture en membrane bitumineuse surchauffe', text: \"Le bitume est foncé, et une surface foncée absorbe l'essentiel du rayonnement solaire qu'elle reçoit. En plein été, une membrane bitumineuse peut atteindre 80°C en surface. Cette chaleur traverse ensuite la toiture et se diffuse à l'intérieur du bâtiment.\" },\n        { title: 'Le cool roof sur membrane bitumineuse, comment ça marche', text: \"Le cool roof consiste à appliquer un revêtement clair à haute réflectance solaire directement sur l'étanchéité existante. Covalba utilise le système CovaTherm : primaire, base coat polyuréthane et top coat de finition.\" },\n        { title: \"Peinture cool roof ou réfection d'étanchéité : le bon calcul\", text: \"Refaire une étanchéité coûte entre 80 et 100 €/m² pour une membrane neuve, et jusqu'à 150 €/m² si l'isolation est reprise. Un cool roof se situe autour de 20 à 25 €/m² et repousse le remplacement.\" },\n        { title: 'Quand opter pour CovaSeal 20', text: \"Lorsque l'étanchéité vieillit, farine ou présente des microfissures sans fuite, CovaSeal 20 renforce l'étanchéité existante et rafraîchit la toiture en une seule opération.\" },\n        { title: 'Un principe validé par la recherche', text: \"Les laboratoires publics documentent depuis longtemps l'efficacité des toitures réfléchissantes. Ces ordres de grandeur expliquent les gains mesurés sur les grandes toitures industrielles.\" },\n        { title: 'Une protection qui dure', text: \"Le système CovaTherm, en polyuréthane avec vernis de finition, est conçu pour conserver ses performances 8 à 10 ans. C'est cette longévité qui transforme le cool roof en investissement.\" },\n      ],\n      sources: commonSources,\n    },\n    productName: 'CovaTherm',\n  },\n  {\n    slug: 'bac-acier',\n    path: '/toitures/bac-acier',\n    title: 'Cool roof bac acier : -10°C et anticorrosion | Covalba',\n    description: 'Cool roof pour toiture bac acier : -8 à -10°C sous toiture et protection anticorrosion en une passe, pour 20-25 €/m².',\n    canonical: 'https://www.covalba.fr/toitures/bac-acier',\n    eyebrow: 'COOL ROOF BAC ACIER',\n    breadcrumb: 'Bac acier',\n    h1: 'Cool roof pour toiture en bac acier',\n    lead: 'Rafraîchissez votre toiture bac acier de 8 à 10°C et stoppez la corrosion, sans changer la tôle.',\n    heroImage: compatBacAcier,\n    heroImageAlt: 'Toiture en bac acier exposée au soleil avant traitement cool roof anticorrosion',\n    heroStats: [\n      { value: '-8 à -10°C', label: 'sous toiture' },\n      { value: 'Anticorrosion', label: '+ réflectif en une passe' },\n      { value: '20-25 €/m²', label: 'sans dépose de tôle' },\n    ],\n    reassurance: ['Pose sécurisée, machines au pied', 'Compatible acier, galva et laqué', 'Traite corrosion et chaleur', 'Sans interrompre votre activité'],\n    problem: {\n      badge: 'Pourquoi agir',\n      title: 'Le bac acier chauffe vite, rouille vite, et fatigue vite.',\n      cards: [\n        { title: 'Le métal conduit la chaleur', text: \"Une tôle sombre monte très haut en température au soleil et transmet cette chaleur à l'intérieur presque instantanément.\" },\n        { title: \"La corrosion s'installe\", text: \"Aux recouvrements, aux fixations et là où la peinture d'origine s'efface, la rouille gagne et menace l'étanchéité.\" },\n        { title: 'Dilatation et condensation', text: 'Les écarts de température font travailler la tôle et favorisent la condensation côté intérieur.' },\n      ],\n      transition: 'Une seule intervention peut traiter la chaleur et la corrosion en même temps.',\n    },\n    solution: {\n      badge: 'La solution Covalba',\n      title: 'Votre toiture renvoie le soleil et cesse de rouiller.',\n      text: \"Sur une toiture bac acier, Covalba applique CovaMetal 20, une peinture anticorrosion réfléchissante : elle protège le métal de la rouille tout en renvoyant le rayonnement solaire. Une finition CovaTherm complète le système pour une réflectivité durable.\",\n      systemTitle: 'Le système',\n      system: ['Préparation et traitement des points de corrosion', 'CovaMetal 20 anticorrosion réfléchissant', 'Finition réflective CovaTherm pour la tenue dans le temps'],\n      cta: { label: 'Voir la fiche CovaMetal 20', href: '/solutions/covametal-20' },\n    },\n    benefits: {\n      badge: 'Ce que vous y gagnez',\n      title: 'Plus frais, protégé de la rouille, prolongé.',\n      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n      cards: [\n        { title: 'Un atelier ou un entrepôt tempéré', text: 'La tôle ne transmet plus sa chaleur. Les espaces sous bac acier redeviennent supportables l’été.' },\n        { title: 'La corrosion stoppée', text: 'Le métal est protégé, la rouille existante traitée, la progression bloquée.' },\n        { title: 'Une tôle prolongée', text: 'En traitant corrosion et chaleur, vous repoussez de plusieurs années le remplacement de la couverture.' },\n        { title: 'Une facture allégée', text: 'Sur les bâtiments climatisés, les groupes de froid travaillent moins.' },\n      ],\n    },\n    roi: {\n      badge: 'Le bon calcul',\n      title: 'Traiter la tôle avant de devoir la changer.',\n      intro: 'Tant que le métal reste sain, le traitement anticorrosion réfléchissant protège, rafraîchit et évite une dépose lourde.',\n      comparison: {\n        headers: ['', 'Cool roof CovaMetal 20', 'Repeindre en anticorrosion simple', 'Remplacement de la couverture'],\n        rows: [\n          ['Investissement', '~112 500 EUR (22,5 EUR/m2)', 'moins cher au m2', 'bien plus élevé'],\n          ['Rafraîchit le bâtiment', 'oui (-8 à -10°C)', 'non', 'selon le produit posé'],\n          ['Protège de la corrosion', 'oui', 'oui', 'neuf'],\n          ['Activité pendant le chantier', 'maintenue', 'maintenue', 'interrompue'],\n        ],\n      },\n      afterComparison: 'La différence se joue sur le double bénéfice : corrosion traitée et chaleur réduite.',\n      situation: {\n        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n        rows: [\n          ['Tôle saine exposée à la chaleur', 'Cool roof CovaTherm', 'Le métal est bon, on rafraîchit simplement et durablement.'],\n          ['Corrosion qui démarre', 'CovaMetal 20 + finition CovaTherm', 'Corrosion et chaleur traitées en une passe, tôle prolongée.'],\n          ['Corrosion perforante / tôle percée', \"Remplacement des tôles touchées, puis traitement de l'ensemble\", \"On ne peint pas sur une tôle morte. On reprend d'abord le ponctuel.\"],\n          ['Forte condensation', 'Étude préalable', 'Traiter la cause avant la surface.'],\n        ],\n      },\n      cta: 'Demander un devis',\n    },\n    midCta: {\n      title: 'Refroidissez votre bac acier tout en bloquant la corrosion.',\n      text: \"On vérifie l'état du métal, les fixations et les départs de rouille pour chiffrer un traitement qui protège et rafraîchit en une opération.\",\n    },\n    compatibility: {\n      badge: 'Sur quoi on intervient',\n      title: 'Compatible acier, galvanisé et laqué, après étude technique.',\n      text: \"Le système s'applique sur les couvertures et bardages en bac acier, qu'ils soient bruts, galvanisés ou prélaqués. On commence par une étude de l'état du métal et des points de corrosion. La rouille est traitée localement, puis CovaMetal 20 est appliqué avant la finition réflective.\",\n      image: compatBacAcier,\n    },\n    method: {\n      badge: 'Chantier en sécurité',\n      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n      steps: [\n        { title: 'Étude technique', text: 'État du métal, corrosion, points singuliers, objectifs thermiques.' },\n        { title: 'Préparation', text: 'Nettoyage et traitement des points de corrosion.' },\n        { title: 'Application sécurisée', text: \"CovaMetal 20 puis finition, projection depuis l'extérieur.\" },\n        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n      ],\n      reassurance: \"Machines au pied du bâtiment, rien sur le toit. Aucun risque pour la structure ni pour l'étanchéité, applicateurs formés et équipés.\",\n    },\n    proof: {\n      badge: 'Des résultats concrets',\n      title: 'Mesuré sur le terrain.',\n      figures: [\n        { value: '-7,5°C', label: 'sur la température de surface' },\n        { value: 'Corrosion stoppée', label: 'sur les zones traitées' },\n        { value: '20 %', label: 'de nos chantiers réalisés sur bac acier' },\n      ],\n    },\n    faq: [\n      { q: \"Ma tôle est déjà rouillée, c'est trop tard ?\", a: \"Non, tant que la corrosion n'a pas perforé le métal. On traite la rouille existante avant d'appliquer CovaMetal 20.\" },\n      { q: 'Ça tient sur du galvanisé ou du laqué ?', a: \"Oui, après préparation adaptée. L'étude technique vérifie l'adhérence et définit la bonne préparation selon le revêtement d'origine.\" },\n      { q: 'Le cool roof réduit-il la condensation ?', a: \"Il agit surtout sur la chaleur d'été. La condensation dépend aussi de la ventilation et de l'isolation.\" },\n      { q: 'Et les recouvrements et les fixations ?', a: \"Ce sont les points sensibles, traités spécifiquement pendant la préparation, car c'est là que la corrosion démarre le plus souvent.\" },\n      { q: \"Et l'hiver, je vais consommer plus de chauffage ?\", a: \"L'effet est minime. En hiver le soleil est bas et peu présent. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n    ],\n    finalCta: {\n      title: 'Protégez votre bac acier de la chaleur et de la rouille.',\n      text: \"CovaMetal 20 traite la corrosion et réduit la chaleur transmise au bâtiment, sans déposer la couverture.\",\n      reassurances: ['Étude sous 48h', 'Traite corrosion et chaleur', 'Sans engagement'],\n    },\n    seo: {\n      intro: 'Le bac acier équipe une grande partie des toitures et bardages industriels, logistiques et commerciaux. Léger et économique, il a deux faiblesses bien connues : il conduit la chaleur et il rouille.',\n      sections: [\n        { title: 'Pourquoi une toiture en bac acier surchauffe et se corrode', text: \"Le métal est un excellent conducteur thermique. Sous le soleil, une tôle sombre monte très vite en température et transmet cette chaleur à l'intérieur presque sans inertie. Le cycle chaleur-condensation accélère ensuite la corrosion.\" },\n        { title: 'Le cool roof sur bac acier, comment ça marche', text: 'Covalba applique CovaMetal 20, une peinture anticorrosion réfléchissante qui protège le métal tout en renvoyant le rayonnement solaire, puis une finition CovaTherm pour une réflectivité durable.' },\n        { title: 'Peinture réfléchissante bac acier ou remplacement de couverture', text: \"Remplacer une couverture en bac acier est une opération lourde. Une peinture cool roof bac acier coûte une fraction de ce prix, se pose sans arrêt d'activité et prolonge la couverture existante.\" },\n        { title: 'Quand la tôle est trop corrodée', text: 'Le cool roof protège un métal sain ou en début de corrosion, il ne sauve pas une tôle perforée. Les tôles concernées doivent être remplacées avant traitement.' },\n        { title: 'Un principe validé par la recherche', text: \"L'efficacité des surfaces réfléchissantes est documentée par les laboratoires publics. Sur une grande toiture en bac acier, fortement exposée, ces effets se traduisent directement en confort et en économies.\" },\n        { title: 'Une protection qui dure', text: \"Sur du métal, la tenue du revêtement est déterminante. Le système CovaMetal 20 et sa finition CovaTherm sont conçus pour protéger et réfléchir 8 à 10 ans.\" },\n      ],\n      sources: commonSources,\n    },\n    productName: 'CovaMetal 20',\n  },\n  {\n    slug: 'toiture-plate',\n    path: '/toitures/toiture-plate',\n    title: 'Cool roof toiture plate et terrasse : -10°C, étanchéité',\n    description: 'Cool roof pour toiture plate et terrasse : -8 à -10°C sous toiture et étanchéité renforcée avec CovaSeal 20, pour 20-25 €/m².',\n    canonical: 'https://www.covalba.fr/toitures/toiture-plate',\n    eyebrow: 'COOL ROOF TOITURE PLATE',\n    breadcrumb: 'Toiture plate et terrasse',\n    h1: 'Cool roof pour toiture plate et terrasse',\n    lead: 'Rafraîchissez votre toiture-terrasse de 8 à 10°C et sécurisez son étanchéité, sans tout refaire.',\n    heroImage: covaSealHero,\n    heroImageAlt: 'Application de CovaSeal 20 sur une toiture plate et terrasse',\n    heroStats: [\n      { value: '-8 à -10°C', label: 'sous toiture' },\n      { value: 'Étanchéité', label: '+ fraîcheur en une opération' },\n      { value: '20-25 €/m²', label: 'contre 80-150 €' },\n    ],\n    reassurance: ['Pose sécurisée, machines au pied', 'Compatible terrasses étanchées', 'Étanchéité liquide réfléchissante', \"Sans interrompre l'activité\"],\n    problem: {\n      badge: 'Pourquoi agir',\n      title: 'À plat, votre toiture prend le soleil toute la journée.',\n      cards: [\n        { title: 'Une exposition maximale', text: \"Une toiture plate reçoit le rayonnement solaire de plein fouet du matin au soir. C'est la surface la plus chaude du bâtiment.\" },\n        { title: 'Une étanchéité sous tension', text: \"Chaleur, UV et stagnation d'eau fatiguent l'étanchéité d'une terrasse. Les points singuliers sont les premiers à lâcher.\" },\n        { title: 'Un confort qui se dégrade', text: 'Sous une terrasse surchauffée, les derniers niveaux deviennent difficiles à vivre l’été, et la climatisation s’emballe.' },\n      ],\n      transition: 'Une seule solution peut rafraîchir la terrasse et renforcer son étanchéité.',\n    },\n    solution: {\n      badge: 'La solution Covalba',\n      title: 'Votre terrasse renvoie le soleil et reste étanche.',\n      text: \"Sur une toiture-terrasse, Covalba applique CovaSeal 20, une étanchéité liquide réfléchissante : elle renforce l'étanchéité existante tout en renvoyant le rayonnement solaire. Si le support est déjà parfaitement étanche, le système CovaTherm suffit à rafraîchir.\",\n      systemTitle: 'Le système',\n      system: [\"Étude de l'étanchéité et des points singuliers\", 'CovaSeal 20 ou CovaTherm selon l’état', 'Traitement soigné des relevés, évacuations et joints'],\n      cta: { label: 'Voir la fiche CovaSeal 20', href: '/solutions/covaseal-20' },\n    },\n    benefits: {\n      badge: 'Ce que vous y gagnez',\n      title: 'Fraîche dessus, étanche dessous.',\n      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n      cards: [\n        { title: 'Des niveaux supérieurs tempérés', text: 'La terrasse ne renvoie plus sa chaleur vers le bas. Les espaces sous toiture redeviennent confortables.' },\n        { title: 'Une étanchéité renforcée', text: \"CovaSeal 20 conforte l'étanchéité existante et sécurise les points sensibles.\" },\n        { title: 'Une réfection repoussée', text: 'Vous décalez de plusieurs années une réfection lourde et coûteuse.' },\n        { title: 'Une facture allégée', text: 'Sur les bâtiments climatisés, les besoins de froid baissent.' },\n      ],\n    },\n    roi: {\n      badge: 'Le bon calcul',\n      title: 'Sécuriser la terrasse avant la réfection lourde.',\n      intro: \"Tant que la structure reste saine, l'étanchéité liquide réfléchissante renforce et rafraîchit sans refaire toute la terrasse.\",\n      comparison: defaultRoiComparison,\n      afterComparison: \"Vous gagnez en confort d'été tout en repoussant une intervention beaucoup plus lourde.\",\n      situation: {\n        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n        rows: [\n          ['Étanchéité saine exposée à la chaleur', 'Cool roof CovaTherm', \"On rafraîchit et on protège l'étanchéité, investissement minimal.\"],\n          ['Étanchéité vieillissante sans fuite', 'CovaSeal 20', \"Renforce l'étanchéité et rafraîchit pour environ un quart du prix d'une réfection.\"],\n          ['Fuites actives', 'Reprise des points singuliers puis CovaSeal 20, ou réfection si trop dégradée', 'On sécurise d’abord, on ne couvre pas une fuite.'],\n          ['Isolation insuffisante', \"Cool roof pour l'été, à comparer avec une réfection complète si l'isolation est le sujet\", \"Le cool roof agit sur l'été, pas sur l'isolation.\"],\n        ],\n      },\n      cta: 'Demander un devis',\n    },\n    midCta: {\n      title: 'Rafraîchissez votre terrasse tout en sécurisant l’étanchéité.',\n      text: \"On vérifie les relevés, évacuations et zones de stagnation pour choisir entre CovaSeal 20, CovaTherm ou une reprise plus lourde.\",\n    },\n    compatibility: {\n      badge: 'Sur quoi on intervient',\n      title: 'Compatible avec la plupart des terrasses étanchées.',\n      text: \"Le système s'applique sur les toitures-terrasses en membrane bitumineuse, en membrane synthétique ou sur support béton étanché. Tout commence par une étude de l'étanchéité, des relevés, des évacuations et des zones de stagnation.\",\n      image: covaSealHero,\n    },\n    method: {\n      badge: 'Chantier en sécurité',\n      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n      steps: [\n        { title: 'Étude technique', text: \"État de l'étanchéité, points singuliers, stagnation, objectifs thermiques.\" },\n        { title: 'Préparation', text: 'Nettoyage et reprise des points sensibles.' },\n        { title: 'Application sécurisée', text: \"CovaSeal 20 ou CovaTherm, projection depuis l'extérieur.\" },\n        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n      ],\n      reassurance: \"Machines au pied du bâtiment, rien sur le toit. Aucun risque pour la structure ni pour l'étanchéité, applicateurs formés et équipés.\",\n    },\n    proof: {\n      badge: 'Des résultats concrets',\n      title: 'Mesuré sur le terrain.',\n      figures: [\n        { value: '-7,5°C', label: 'sur la température de surface' },\n        { value: 'Étanchéité renforcée', label: 'sur les zones traitées' },\n        { value: '+8 à 10 ans', label: \"d'étanchéité préservée\" },\n      ],\n    },\n    faq: [\n      { q: 'Quelle différence entre CovaTherm et CovaSeal 20 sur ma terrasse ?', a: \"CovaTherm rafraîchit une étanchéité encore saine. CovaSeal 20 renforce l'étanchéité tout en rafraîchissant.\" },\n      { q: \"Ça gère la stagnation d'eau ?\", a: \"L'étude technique identifie les zones de stagnation et les points singuliers, qui sont repris avant application.\" },\n      { q: \"Combien de temps ça prolonge mon étanchéité ?\", a: 'En la protégeant des UV et de la chaleur, vous gagnez en général 8 à 10 ans avant une réfection.' },\n      { q: \"Faut-il déposer l'ancienne étanchéité ?\", a: 'Non. Un nettoyage et la reprise des points singuliers suffisent.' },\n      { q: \"Et l'hiver ?\", a: \"L'effet est minime. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n    ],\n    finalCta: {\n      title: 'Gagnez en fraîcheur sans refaire toute la terrasse.',\n      text: \"CovaSeal 20 renforce l'étanchéité existante et réduit la chaleur sous toiture, avec un chantier extérieur sans interruption d'activité.\",\n      reassurances: ['Étude sous 48h', 'Étanchéité et fraîcheur', 'Sans engagement'],\n    },\n    seo: {\n      intro: \"La toiture plate, ou toiture-terrasse, est la configuration la plus exposée au soleil. Elle concentre deux enjeux : la surchauffe du bâtiment et la durabilité de l'étanchéité.\",\n      sections: [\n        { title: 'Pourquoi une toiture plate surchauffe', text: \"Une toiture-terrasse reçoit le soleil de face une grande partie de la journée. La surface, souvent sombre, peut dépasser 80°C en été, et cette chaleur descend directement vers les niveaux situés en dessous.\" },\n        { title: 'Le cool roof sur toiture plate, comment ça marche', text: \"La peinture réfléchissante renvoie le rayonnement au lieu de l'absorber. Sur une terrasse, Covalba privilégie souvent CovaSeal 20 pour renforcer l'étanchéité et rafraîchir en une seule opération.\" },\n        { title: \"Cool roof toiture terrasse ou réfection d'étanchéité\", text: \"Refaire l'étanchéité d'une terrasse coûte de 80 à 100 €/m², et jusqu'à 150 €/m² avec isolation. Une solution cool roof se situe autour de 20 à 25 €/m².\" },\n        { title: \"Quand choisir l'étanchéité liquide réfléchissante\", text: \"Une étanchéité encore en place mais fatiguée, sans fuite déclarée, est la situation idéale pour CovaSeal 20.\" },\n        { title: 'Un principe validé par la recherche', text: \"Sur une terrasse, surface la plus exposée du bâtiment, l'effet des toitures réfléchissantes est au maximum de son potentiel.\" },\n        { title: 'Une protection qui dure', text: 'Les systèmes Covalba, CovaSeal 20 et CovaTherm, sont conçus pour tenir 8 à 10 ans.' },\n      ],\n      sources: commonSources,\n    },\n    productName: 'CovaSeal 20',\n  },\n  {\n    slug: 'fibrociment',\n    path: '/toitures/fibrociment',\n    title: 'Cool roof fibrociment : -10°C sans dépose | Covalba',\n    description: 'Cool roof pour toiture fibrociment : -8 à -10°C sous toiture sans dépose, pour 20-25 €/m². Alternative à un chantier de dépose coûteux.',\n    canonical: 'https://www.covalba.fr/toitures/fibrociment',\n    eyebrow: 'COOL ROOF FIBROCIMENT',\n    breadcrumb: 'Fibrociment',\n    h1: 'Cool roof pour toiture en fibrociment',\n    lead: 'Rafraîchissez votre toiture fibrociment de 8 à 10°C sans la déposer, et évitez un chantier de dépose coûteux.',\n    heroImage: compatFibrociment,\n    heroImageAlt: 'Toiture fibrociment ancienne traitable avec préparation douce',\n    heroStats: [\n      { value: '-8 à -10°C', label: 'sous toiture' },\n      { value: 'Sans dépose', label: 'de la couverture' },\n      { value: '20-25 €/m²', label: 'sans gros chantier' },\n    ],\n    reassurance: ['Pose sécurisée, machines au pied', 'Application sans agression du support', 'Sans dépose', \"Sans interrompre l'activité\"],\n    problem: {\n      badge: 'Pourquoi agir',\n      title: 'Le fibrociment chauffe, vieillit, et coûte cher à déposer.',\n      cards: [\n        { title: 'Il absorbe et transmet la chaleur', text: \"Les plaques de fibrociment, souvent sombres et peu isolantes, chauffent fort au soleil.\" },\n        { title: 'Il se dégrade avec le temps', text: 'Le fibrociment devient poreux, se couvre de mousses et perd son aspect.' },\n        { title: 'Le déposer coûte très cher', text: 'Sur les couvertures anciennes, la dépose est un chantier lourd et coûteux, surtout si le matériau relève de la réglementation amiante.' },\n      ],\n      transition: 'Le cool roof permet de rafraîchir et protéger sans toucher à la couverture.',\n    },\n    solution: {\n      badge: 'La solution Covalba',\n      title: 'Votre toiture renvoie le soleil, sans dépose.',\n      text: 'Sur une toiture fibrociment, Covalba applique le système CovaTherm avec une préparation douce, adaptée à la fragilité du support. Le revêtement réfléchissant renvoie le rayonnement solaire, fait baisser la température et protège les plaques, sans dépose ni perçage.',\n      systemTitle: 'Le système',\n      system: [\"Étude de l'état des plaques et préparation adaptée\", 'Revêtement réfléchissant CovaTherm', 'Laque solaire sur les lanterneaux si besoin'],\n      note: \"Sur fibrociment susceptible de contenir de l'amiante, l'application relève de la réglementation en vigueur. Le cadre est précisé lors de l'étude technique.\",\n      cta: { label: 'Voir la fiche CovaTherm', href: '/solutions/covatherm' },\n    },\n    benefits: {\n      badge: 'Ce que vous y gagnez',\n      title: 'Plus frais, protégé, sans gros chantier.',\n      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n      cards: [\n        { title: 'Un bâtiment tempéré', text: 'Le fibrociment ne transmet plus sa chaleur. Hangars et ateliers redeviennent supportables l’été.' },\n        { title: 'Une couverture protégée', text: 'Le revêtement protège les plaques des UV et limite leur vieillissement.' },\n        { title: 'Pas de dépose', text: 'Vous évitez un chantier lourd et le surcoût associé aux matériaux anciens.' },\n        { title: 'Une intervention rapide', text: \"Application depuis l'extérieur, sans interrompre l'activité du site.\" },\n      ],\n    },\n    roi: {\n      badge: 'Le bon calcul',\n      title: 'Protéger les plaques plutôt que déposer.',\n      intro: 'Tant que les plaques sont saines, le cool roof réduit la chaleur et évite un chantier de dépose lourd.',\n      afterComparison: \"Sur fibrociment, l'écart de coût avec la dépose est souvent le plus marqué.\",\n      situation: {\n        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n        rows: [\n          ['Plaques saines exposées à la chaleur', 'Cool roof CovaTherm, préparation douce', 'On rafraîchit et on protège sans toucher à la couverture.'],\n          ['Plaques poreuses, mousses', 'Nettoyage doux adapté puis CovaTherm', 'On régénère l’aspect et on protège, sans dépose.'],\n          ['Plaques cassées ou fissurées', \"Remplacement ponctuel des plaques, puis traitement de l'ensemble\", 'On reprend le ponctuel avant de protéger le reste.'],\n          ['Couverture en fin de vie', 'Étude technique : la dépose peut s’imposer', 'Le cool roof ne sauve pas une couverture condamnée.'],\n        ],\n      },\n      cta: 'Demander un devis',\n    },\n    midCta: {\n      title: 'Rafraîchissez votre fibrociment sans dépose.',\n      text: \"On vérifie l'état des plaques et le cadre réglementaire pour traiter la chaleur sans engager un chantier lourd de dépose.\",\n    },\n    compatibility: {\n      badge: 'Sur quoi on intervient',\n      title: 'Une préparation douce, adaptée à un support fragile.',\n      text: 'Le fibrociment est un support fragile qui ne supporte pas les traitements agressifs. La préparation est donc adaptée : nettoyage doux, sans projection à haute pression susceptible de dégrader les plaques, et sans perçage.',\n      image: compatFibrociment,\n    },\n    method: {\n      badge: 'Chantier en sécurité',\n      title: \"Tout se passe depuis l'extérieur, sans monter de machine sur le toit.\",\n      steps: [\n        { title: 'Étude technique', text: 'État des plaques, mousses, solidité, contexte réglementaire.' },\n        { title: 'Préparation', text: 'Nettoyage doux adapté, sans agresser le support.' },\n        { title: 'Application sécurisée', text: \"Revêtement réfléchissant, projection depuis l'extérieur.\" },\n        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n      ],\n      reassurance: 'Machines au pied du bâtiment, rien sur le toit. Application sans agression du support, applicateurs formés et équipés.',\n    },\n    proof: {\n      badge: 'Des résultats concrets',\n      title: 'Mesuré sur le terrain.',\n      figures: [\n        { value: '-7,5°C', label: 'sur la température de surface' },\n        { value: 'Plaques protégées', label: 'des UV et du vieillissement' },\n        { value: 'Sans dépose', label: 'de la couverture' },\n      ],\n    },\n    faq: [\n      { q: \"Et si mon fibrociment contient de l'amiante ?\", a: \"L'application d'un revêtement sur fibrociment amianté relève de la réglementation sur l'amiante. L'étude technique précise le cadre applicable à votre toiture.\" },\n      { q: 'Ça tient sur un fibrociment poreux ?', a: 'Oui, après une préparation adaptée. Un fibrociment poreux ou couvert de mousses est nettoyé en douceur avant application.' },\n      { q: 'Faut-il déposer les plaques ?', a: \"Non, c'est tout l'intérêt. Seules les plaques cassées sont remplacées ponctuellement.\" },\n      { q: 'Pourquoi ne pas nettoyer à haute pression ?', a: \"Parce que le fibrociment est fragile et qu'un nettoyage agressif le dégrade.\" },\n      { q: \"Et l'hiver ?\", a: \"L'effet est minime. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n    ],\n    finalCta: {\n      title: 'Évitez la dépose quand les plaques peuvent être protégées.',\n      text: \"CovaTherm rafraîchit et protège le fibrociment avec une préparation douce, adaptée au support et à son cadre réglementaire.\",\n      reassurances: ['Étude sous 48h', 'Sans dépose', 'Sans engagement'],\n    },\n    seo: {\n      intro: 'Le fibrociment couvre une grande partie des hangars industriels et agricoles construits au siècle dernier. Il chauffe fort, vieillit mal, et sa dépose est devenue coûteuse.',\n      sections: [\n        { title: 'Pourquoi une toiture en fibrociment surchauffe', text: \"Les plaques de fibrociment, souvent sombres et faiblement isolantes, absorbent le rayonnement solaire et le transmettent à l'intérieur.\" },\n        { title: 'Le cool roof sur fibrociment, comment ça marche', text: 'CovaTherm est appliqué avec une préparation douce : pas de nettoyage agressif, pas de perçage. Le revêtement rafraîchit et protège les plaques du vieillissement.' },\n        { title: \"Le cas de l'amiante\", text: \"Une grande partie des toitures fibrociment anciennes contiennent de l'amiante. Dans ce cas, l'intervention doit suivre la réglementation applicable, précisée toiture par toiture.\" },\n        { title: 'Cool roof fibrociment ou dépose', text: \"Déposer une couverture ancienne, l'évacuer puis poser une nouvelle toiture représente un budget très élevé. Tant que les plaques sont saines, le cool roof est l'option la plus rentable.\" },\n        { title: 'Un principe validé par la recherche', text: \"Sur un hangar fibrociment mal isolé, l'effet des surfaces réfléchissantes se ressent rapidement sur le confort d'été.\" },\n        { title: 'Une protection qui dure', text: \"Le système CovaTherm, en polyuréthane avec vernis de finition, est conçu pour tenir 8 à 10 ans tout en protégeant les plaques.\" },\n      ],\n      sources: commonSources,\n    },\n    productName: 'CovaTherm',\n  },\n  {\n    slug: 'tuiles-ciment',\n    path: '/toitures/tuiles-ciment',\n    title: 'Cool roof tuiles ciment : -10°C dans les combles | Covalba',\n    description: 'Cool roof pour toiture en tuiles ciment : -8 à -10°C sous toiture, des combles plus frais sans dépose, pour 20-25 €/m².',\n    canonical: 'https://www.covalba.fr/toitures/tuiles-ciment',\n    eyebrow: 'COOL ROOF TUILES CIMENT',\n    breadcrumb: 'Tuiles ciment',\n    h1: 'Cool roof pour toiture en tuiles ciment',\n    lead: 'Rafraîchissez vos combles de 8 à 10°C en traitant la tuile, sans rénovation lourde.',\n    heroImage: compatTuile,\n    heroImageAlt: 'Toiture en tuiles ciment compatible avec revêtement réfléchissant',\n    heroStats: [\n      { value: '-8 à -10°C', label: 'sous toiture' },\n      { value: 'Sans dépose', label: 'des tuiles' },\n      { value: '20-25 €/m²', label: 'sans gros chantier' },\n    ],\n    reassurance: ['Pose sécurisée, machines au pied', 'Adapté aux supports poreux', 'Sans dépose', \"Sans interrompre l'usage du bâtiment\"],\n    problem: {\n      badge: 'Pourquoi agir',\n      title: 'Des tuiles ciment foncées, des combles en surchauffe.',\n      cards: [\n        { title: 'Elles emmagasinent la chaleur', text: 'Les tuiles ciment accumulent la chaleur du soleil et la diffusent vers les combles et les niveaux sous toiture.' },\n        { title: 'Elles deviennent poreuses', text: 'Avec les années, la tuile ciment se patine, devient poreuse et se couvre de mousses.' },\n        { title: 'Les combles surchauffent', text: 'Sous une toiture exposée, les combles et les derniers étages deviennent difficiles à occuper l’été.' },\n      ],\n      transition: 'Le cool roof traite la tuile pour rafraîchir, sans rénovation lourde.',\n    },\n    solution: {\n      badge: 'La solution Covalba',\n      title: 'Vos tuiles renvoient le soleil au lieu de le stocker.',\n      text: 'Sur une toiture en tuiles ciment, Covalba applique le système CovaTherm, adapté aux supports poreux. Le revêtement réfléchissant renvoie le rayonnement solaire, fait baisser la température des combles et protège la tuile, sans dépose.',\n      systemTitle: 'Le système',\n      system: ['Nettoyage et traitement des mousses', 'Revêtement réfléchissant CovaTherm', 'Laque solaire sur les ouvertures vitrées si besoin'],\n      cta: { label: 'Voir la fiche CovaTherm', href: '/solutions/covatherm' },\n    },\n    benefits: {\n      badge: 'Ce que vous y gagnez',\n      title: 'Des combles tempérés, une toiture protégée.',\n      featured: \"Jusqu'à -8 à -10°C sous toiture, dès le premier été.\",\n      cards: [\n        { title: 'Des combles plus frais', text: 'La tuile ne diffuse plus sa chaleur. Les espaces sous toiture redeviennent confortables l’été.' },\n        { title: 'Une tuile protégée', text: 'Le revêtement protège la tuile de la porosité, des mousses et des UV.' },\n        { title: 'Un aspect rénové', text: 'La toiture retrouve un aspect propre et uniforme, sans remplacement des tuiles.' },\n        { title: 'Sans gros chantier', text: 'Application depuis l’extérieur, sans dépose ni rénovation lourde.' },\n      ],\n    },\n    roi: {\n      badge: 'Le bon calcul',\n      title: 'Rafraîchir les combles sans refaire la toiture.',\n      intro: 'Tant que les tuiles sont saines, le cool roof limite la chaleur et protège la couverture sans rénovation lourde.',\n      situation: {\n        headers: ['Votre situation', \"Ce qu'on recommande\", \"Pourquoi c'est le bon calcul\"],\n        rows: [\n          ['Tuiles saines, combles chauds', 'Cool roof CovaTherm', 'On rafraîchit et on protège la tuile, investissement léger.'],\n          ['Tuiles poreuses, mousses', 'Nettoyage et traitement puis CovaTherm', 'On régénère l’aspect et on protège, sans dépose.'],\n          ['Tuiles cassées', \"Remplacement ponctuel puis traitement de l'ensemble\", 'On reprend le ponctuel avant de protéger le reste.'],\n          ['Combles à rénover en profondeur', 'Comparer avec une isolation si le confort hiver est aussi en jeu', \"Le cool roof agit sur l'été, pas sur l'isolation.\"],\n        ],\n      },\n      cta: 'Demander un devis',\n    },\n    midCta: {\n      title: 'Rafraîchissez vos combles sans changer les tuiles.',\n      text: \"On vérifie la porosité, les mousses et les tuiles cassées pour chiffrer un traitement qui protège la couverture et limite la chaleur.\",\n    },\n    compatibility: {\n      badge: 'Sur quoi on intervient',\n      title: 'Adapté à la tuile ciment, un support poreux.',\n      text: \"La tuile ciment est un support poreux qui demande une préparation soignée. On évalue l'état de la couverture, on traite les mousses et les lichens, on nettoie, puis on applique le revêtement réfléchissant adapté.\",\n      image: compatTuile,\n    },\n    method: {\n      badge: 'Chantier en sécurité',\n      title: \"Tout se passe depuis l'extérieur, en sécurité.\",\n      steps: [\n        { title: 'Étude technique', text: 'État des tuiles, mousses, porosité, objectifs thermiques.' },\n        { title: 'Préparation', text: 'Nettoyage, traitement des mousses, remplacement ponctuel si besoin.' },\n        { title: 'Application sécurisée', text: 'Revêtement réfléchissant adapté au support.' },\n        { title: 'Contrôle', text: 'Vérification de la qualité et suivi dans le temps.' },\n      ],\n      reassurance: 'Machines au pied du bâtiment, applicateurs formés et équipés, travail en sécurité.',\n    },\n    proof: {\n      badge: 'Des résultats concrets',\n      title: 'Mesuré sur le terrain.',\n      figures: [\n        { value: '-7,5°C', label: 'sur la température de surface' },\n        { value: 'Combles rafraîchis', label: 'sous toiture exposée' },\n        { value: 'Sans dépose', label: 'de la couverture' },\n      ],\n    },\n    faq: [\n      { q: 'Ça tient sur une tuile poreuse ?', a: 'Oui, après une préparation adaptée. La tuile est nettoyée et les mousses traitées avant application.' },\n      { q: 'Faut-il remplacer les tuiles ?', a: 'Non, sauf les tuiles cassées, reprises ponctuellement.' },\n      { q: 'Le cool roof isole-t-il mes combles ?', a: \"Il agit sur la chaleur d'été. Il ne remplace pas une isolation, qui joue sur le confort d'hiver.\" },\n      { q: \"Et l'hiver ?\", a: \"L'effet est minime. La légère perte d'intersaison est largement compensée par le gain d'été.\" },\n    ],\n    finalCta: {\n      title: 'Des combles plus frais sans rénovation lourde.',\n      text: \"CovaTherm réfléchit le soleil, protège les tuiles ciment et améliore le confort d'été sans déposer la couverture.\",\n      reassurances: ['Étude sous 48h', 'Sans dépose', 'Sans engagement'],\n    },\n    seo: {\n      intro: 'La tuile ciment couvre de nombreuses toitures en pente du parc tertiaire, des établissements recevant du public et du résidentiel collectif. Elle emmagasine la chaleur et se patine avec le temps.',\n      sections: [\n        { title: 'Pourquoi une toiture en tuiles ciment surchauffe', text: 'La tuile ciment est un matériau dense qui accumule la chaleur du soleil au fil de la journée et la restitue lentement vers les combles.' },\n        { title: 'Le cool roof sur tuiles ciment, comment ça marche', text: 'La peinture réfléchissante renvoie le rayonnement solaire plutôt que de le stocker. CovaTherm adhère aux supports poreux après nettoyage et traitement des mousses.' },\n        { title: 'Cool roof tuiles ciment ou rénovation de toiture', text: \"Tant que les tuiles sont saines, un cool roof à 20-25 €/m² protège la couverture et rafraîchit les combles pour une fraction du prix.\" },\n        { title: 'Un principe validé par la recherche', text: \"Sur des combles sous tuiles exposées, le gain de confort d'une surface réfléchissante est rapidement ressenti.\" },\n        { title: 'Une protection qui dure', text: 'Le système CovaTherm est conçu pour tenir 8 à 10 ans tout en protégeant la tuile.' },\n      ],\n      sources: commonSources,\n    },\n    productName: 'CovaTherm',\n  },\n];\n\nexport const roofPageBySlug = Object.fromEntries(roofPages.map((page) => [page.slug, page])) as Record<string, RoofPageData>;\n","numLines":722,"startLine":1,"totalLines":722}},"sourceToolAssistantUUID":"cf40b125-c7ac-455b-b570-ebe89e9dee00","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"f6063b22-3869-4339-81b7-e4a645be13bc","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_018K8gNx5bZ4KrN6vibcogdx","type":"message","role":"assistant","content":[{"type":"text","text":"Now I have a complete understanding. Let me check the EN chrome components exist and look at an existing EN page for reference patterns."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2799,"cache_creation_input_tokens":74529,"cache_read_input_tokens":15098,"cache_creation":{"ephemeral_5m_input_tokens":74529,"ephemeral_1h_input_tokens":0},"output_tokens":6,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKSguR3z1g33ZNnP8ik","attributionAgent":"general-purpose","type":"assistant","uuid":"4552f76f-b237-44eb-b75a-c325504db0e2","timestamp":"2026-06-22T00:03:28.935Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"4552f76f-b237-44eb-b75a-c325504db0e2","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_018K8gNx5bZ4KrN6vibcogdx","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019hGT2unqb36XnTs3Qs7Da7","name":"Bash","input":{"command":"ls /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/en/ 2>/dev/null; echo \"---SOLUTIONS---\"; ls /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/pages/en/ 2>/dev/null; echo \"---DATA EN---\"; ls /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/data/en/ 2>/dev/null","description":"List EN components, pages, data"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2799,"cache_creation_input_tokens":74529,"cache_read_input_tokens":15098,"cache_creation":{"ephemeral_5m_input_tokens":74529,"ephemeral_1h_input_tokens":0},"output_tokens":6,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKSguR3z1g33ZNnP8ik","attributionAgent":"general-purpose","type":"assistant","uuid":"94c49790-e75f-49e3-905a-c49a38c4358d","timestamp":"2026-06-22T00:03:31.024Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"94c49790-e75f-49e3-905a-c49a38c4358d","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 2\nFooterEN.tsx\nNavbarEN.tsx\nStickyMobileCTAEN.tsx\nhome\nindustries\nproduct\n---SOLUTIONS---\nCommercialEN.tsx\nCovaThermEN.tsx\nHomeEN.tsx\nindustries\nsolutions\n---DATA EN---","is_error":true,"tool_use_id":"toolu_019hGT2unqb36XnTs3Qs7Da7"}]},"uuid":"98269d86-f2c2-401f-a66e-1d9b274563e7","timestamp":"2026-06-22T00:03:31.104Z","toolUseResult":"Error: Exit code 2\nFooterEN.tsx\nNavbarEN.tsx\nStickyMobileCTAEN.tsx\nhome\nindustries\nproduct\n---SOLUTIONS---\nCommercialEN.tsx\nCovaThermEN.tsx\nHomeEN.tsx\nindustries\nsolutions\n---DATA EN---","sourceToolAssistantUUID":"94c49790-e75f-49e3-905a-c49a38c4358d","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"98269d86-f2c2-401f-a66e-1d9b274563e7","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01WvuuMp9EHL5XsgEqkQMAmM","type":"message","role":"assistant","content":[{"type":"text","text":"The EN chrome components exist. Let me check the EN solutions routes and how an existing EN page handles CTAs/links and SEO meta."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":3213,"cache_read_input_tokens":89627,"cache_creation":{"ephemeral_5m_input_tokens":3213,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKTDWAqKhNcQ9C3pwGY","attributionAgent":"general-purpose","type":"assistant","uuid":"a197312e-2e99-4b08-a49d-ae9bf7901f8c","timestamp":"2026-06-22T00:03:33.538Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"a197312e-2e99-4b08-a49d-ae9bf7901f8c","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01WvuuMp9EHL5XsgEqkQMAmM","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RKvMML6wG5QTfs5LTUeqBG","name":"Bash","input":{"command":"cd /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all\necho \"=== EN solutions pages ===\"; ls src/pages/en/solutions/\necho \"=== routes referencing /en/solutions ===\"; grep -rn \"/en/solutions\" src/ | head -30\necho \"=== CovaThermEN imports/CTA hrefs ===\"; grep -n \"href=\\|PrimaryCTA\\|/en\\|usePageMeta\\|Navbar\\|Footer\\|Sticky\" src/pages/en/solutions/CovaThermEN.tsx 2>/dev/null | head -40","description":"Inspect EN routes and CTA conventions"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":3213,"cache_read_input_tokens":89627,"cache_creation":{"ephemeral_5m_input_tokens":3213,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKTDWAqKhNcQ9C3pwGY","attributionAgent":"general-purpose","type":"assistant","uuid":"2460b5f4-58d5-4fa8-990b-e6394733c5c3","timestamp":"2026-06-22T00:03:36.187Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"2460b5f4-58d5-4fa8-990b-e6394733c5c3","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01RKvMML6wG5QTfs5LTUeqBG","type":"tool_result","content":"=== EN solutions pages ===\nCovaMetalEN.tsx\nCovaSealEN.tsx\nCovaThermLightEN.tsx\n=== routes referencing /en/solutions ===\nsrc/App.tsx:42:import CovaSealEN from \"./pages/en/solutions/CovaSealEN\";\nsrc/App.tsx:43:import CovaMetalEN from \"./pages/en/solutions/CovaMetalEN\";\nsrc/App.tsx:44:import CovaThermLightEN from \"./pages/en/solutions/CovaThermLightEN\";\nsrc/App.tsx:114:          <Route path=\"/en/solutions/covatherm\" element={<CovaThermEN />} />\nsrc/App.tsx:115:          <Route path=\"/en/solutions/covaseal-20\" element={<CovaSealEN />} />\nsrc/App.tsx:116:          <Route path=\"/en/solutions/covametal-20\" element={<CovaMetalEN />} />\nsrc/App.tsx:117:          <Route path=\"/en/solutions/covatherm-light\" element={<CovaThermLightEN />} />\nsrc/pages/en/solutions/CovaMetalEN.tsx:97:const PAGE_URL = 'https://www.covalba.fr/en/solutions/covametal-20';\nsrc/pages/en/solutions/CovaThermLightEN.tsx:70:        <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:105:        <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:109:        <a href=\"/en/solutions/covaseal-20\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:113:        <a href=\"/en/solutions/covametal-20\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:144:const PAGE_URL = `${SITE_URL}/en/solutions/covatherm-light`;\nsrc/pages/en/solutions/CovaThermLightEN.tsx:236:            <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/CovaThermEN.tsx:152:const PAGE_URL = 'https://www.covalba.fr/en/solutions/covatherm';\nsrc/pages/en/solutions/CovaSealEN.tsx:101:const PAGE_URL = `${SITE_URL}/en/solutions/covaseal-20`;\nsrc/components/en/NavbarEN.tsx:13:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:20:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:27:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:34:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:208:                    <a href=\"/en/solutions/covatherm\" className=\"flex items-center gap-1 text-xs font-semibold text-primary hover:gap-2 transition-all\">\nsrc/components/en/product/CovaSealEditorialEN.tsx:99:                href=\"/en/solutions/covatherm\"\nsrc/components/en/product/CovaSealComparisonEN.tsx:150:            href=\"/en/solutions/covametal-20\"\nsrc/components/en/product/CovaThermLightEditorialEN.tsx:99:                href=\"/en/solutions/covatherm\"\nsrc/components/en/FooterEN.tsx:5:      { label: 'CovaTherm', href: '/en/solutions/covatherm' },\nsrc/components/en/FooterEN.tsx:6:      { label: 'CovaSeal', href: '/en/solutions/covatherm' },\nsrc/components/en/FooterEN.tsx:7:      { label: 'CovaMetal', href: '/en/solutions/covatherm' },\nsrc/components/en/FooterEN.tsx:8:      { label: 'CovaTherm Light', href: '/en/solutions/covatherm' },\nsrc/components/en/industries/DistributionSEOContentEN.tsx:60:                    <a href=\"/en/solutions/covatherm\" className=\"font-semibold text-primary underline underline-offset-4\">\nsrc/components/en/industries/DistributionSEOContentEN.tsx:143:                    <a href=\"/en/solutions/covatherm\" className=\"font-semibold text-primary underline underline-offset-4\">\n=== CovaThermEN imports/CTA hrefs ===","is_error":false}]},"uuid":"4a36fe85-8655-4af3-9a17-0f7702689538","timestamp":"2026-06-22T00:03:36.267Z","toolUseResult":{"stdout":"=== EN solutions pages ===\nCovaMetalEN.tsx\nCovaSealEN.tsx\nCovaThermLightEN.tsx\n=== routes referencing /en/solutions ===\nsrc/App.tsx:42:import CovaSealEN from \"./pages/en/solutions/CovaSealEN\";\nsrc/App.tsx:43:import CovaMetalEN from \"./pages/en/solutions/CovaMetalEN\";\nsrc/App.tsx:44:import CovaThermLightEN from \"./pages/en/solutions/CovaThermLightEN\";\nsrc/App.tsx:114:          <Route path=\"/en/solutions/covatherm\" element={<CovaThermEN />} />\nsrc/App.tsx:115:          <Route path=\"/en/solutions/covaseal-20\" element={<CovaSealEN />} />\nsrc/App.tsx:116:          <Route path=\"/en/solutions/covametal-20\" element={<CovaMetalEN />} />\nsrc/App.tsx:117:          <Route path=\"/en/solutions/covatherm-light\" element={<CovaThermLightEN />} />\nsrc/pages/en/solutions/CovaMetalEN.tsx:97:const PAGE_URL = 'https://www.covalba.fr/en/solutions/covametal-20';\nsrc/pages/en/solutions/CovaThermLightEN.tsx:70:        <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:105:        <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:109:        <a href=\"/en/solutions/covaseal-20\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:113:        <a href=\"/en/solutions/covametal-20\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/solutions/CovaThermLightEN.tsx:144:const PAGE_URL = `${SITE_URL}/en/solutions/covatherm-light`;\nsrc/pages/en/solutions/CovaThermLightEN.tsx:236:            <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\nsrc/pages/en/CovaThermEN.tsx:152:const PAGE_URL = 'https://www.covalba.fr/en/solutions/covatherm';\nsrc/pages/en/solutions/CovaSealEN.tsx:101:const PAGE_URL = `${SITE_URL}/en/solutions/covaseal-20`;\nsrc/components/en/NavbarEN.tsx:13:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:20:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:27:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:34:    href: '/en/solutions/covatherm',\nsrc/components/en/NavbarEN.tsx:208:                    <a href=\"/en/solutions/covatherm\" className=\"flex items-center gap-1 text-xs font-semibold text-primary hover:gap-2 transition-all\">\nsrc/components/en/product/CovaSealEditorialEN.tsx:99:                href=\"/en/solutions/covatherm\"\nsrc/components/en/product/CovaSealComparisonEN.tsx:150:            href=\"/en/solutions/covametal-20\"\nsrc/components/en/product/CovaThermLightEditorialEN.tsx:99:                href=\"/en/solutions/covatherm\"\nsrc/components/en/FooterEN.tsx:5:      { label: 'CovaTherm', href: '/en/solutions/covatherm' },\nsrc/components/en/FooterEN.tsx:6:      { label: 'CovaSeal', href: '/en/solutions/covatherm' },\nsrc/components/en/FooterEN.tsx:7:      { label: 'CovaMetal', href: '/en/solutions/covatherm' },\nsrc/components/en/FooterEN.tsx:8:      { label: 'CovaTherm Light', href: '/en/solutions/covatherm' },\nsrc/components/en/industries/DistributionSEOContentEN.tsx:60:                    <a href=\"/en/solutions/covatherm\" className=\"font-semibold text-primary underline underline-offset-4\">\nsrc/components/en/industries/DistributionSEOContentEN.tsx:143:                    <a href=\"/en/solutions/covatherm\" className=\"font-semibold text-primary underline underline-offset-4\">\n=== CovaThermEN imports/CTA hrefs ===","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"2460b5f4-58d5-4fa8-990b-e6394733c5c3","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"4a36fe85-8655-4af3-9a17-0f7702689538","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01W3mbZY4xvGLgVC2ybb9hsE","type":"message","role":"assistant","content":[{"type":"text","text":"EN routes confirmed. The French template links to `/diagnostic` (a France contact route) and `/solutions/*` and `/`. For EN, these become `/en`. Note `page.solution.cta.href` carries product links from data — but the template doesn't actually render `page.solution.cta` (it uses hardcoded \"Voir la fiche...\" only via... let me verify). Looking at the FR template, `page.solution.cta` is never rendered — the SolutionSection uses no CTA from data. So no product links from data are rendered. The hardcoded CTAs all go to `/diagnostic`. For EN I'll route them to `/en`.\n\nLet me check how an EN page sets up `usePageMeta` / JSON-LD to mirror it correctly."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":1958,"cache_read_input_tokens":92840,"cache_creation":{"ephemeral_5m_input_tokens":1958,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKTbhA665q8whTmYFA1","attributionAgent":"general-purpose","type":"assistant","uuid":"58c9e5a4-b899-41c2-8b5e-bca76a14ae82","timestamp":"2026-06-22T00:03:42.103Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"58c9e5a4-b899-41c2-8b5e-bca76a14ae82","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01W3mbZY4xvGLgVC2ybb9hsE","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Fi715bcJkNTXwXFQXciszL","name":"Bash","input":{"command":"cd /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all\nsed -n '90,160p' src/pages/en/solutions/CovaThermLightEN.tsx","description":"Check EN page meta and chrome usage"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":1958,"cache_read_input_tokens":92840,"output_tokens":393,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":1958},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":393,"cache_read_input_tokens":92840,"cache_creation_input_tokens":1958,"cache_creation":{"ephemeral_5m_input_tokens":1958,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CcHKTbhA665q8whTmYFA1","attributionAgent":"general-purpose","type":"assistant","uuid":"1e01ecba-3c4a-498f-a83d-eb970cb8d460","timestamp":"2026-06-22T00:03:43.928Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"1e01ecba-3c4a-498f-a83d-eb970cb8d460","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Fi715bcJkNTXwXFQXciszL","type":"tool_result","content":"  },\n  {\n    q: 'From what proportion of skylights should the roof be treated?',\n    text: 'The more roof surface your skylights occupy, the greater the measurable impact of solar-control treatment on the interior atmosphere. A building with 25% or more of its roof in translucent elements achieves very visible comfort gains after applying CovaTherm Light. Below 20%, it is often more relevant to start by treating the opaque part of the roof with CovaTherm, then complement it with CovaTherm Light if the need remains.',\n  },\n  {\n    q: 'Can CovaTherm Light be applied on glazing or a glass roof?',\n    text: 'No. For glass (windows, glass roofs, sunrooms), the aesthetic finish of a solar-control coating applied by brush or sprayer is not satisfactory, and durability over time is lower. For these substrates, a bonded interior solar film remains the recommended solution. CovaTherm Light is specifically designed for translucent polymers (polycarbonate, polyester), which make up the vast majority of skylights and barrel-vault rooflights in industrial and commercial buildings.',\n  },\n  {\n    q: 'Why does CovaTherm Light last 5 to 8 years and not 20 like the other Covalba products?',\n    text: 'The substrate determines the lifespan of a coating. CovaTherm 20, CovaSeal 20 and CovaMetal 20 are protected by an anti-UV top coat that lets them last two decades on a stable opaque surface. On polycarbonate, the substrate itself ages under the effect of UV, even when protected by a solar-control coating. After 5 to 8 years, the degradation of the underlying polycarbonate requires a system refurbishment or skylight replacement. An annual rinse with clean water extends the reflective performance during this period.',\n    node: (\n      <>\n        The substrate determines the lifespan of a coating.{' '}\n        <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\n          CovaTherm 20\n        </a>\n        ,{' '}\n        <a href=\"/en/solutions/covaseal-20\" className=\"text-primary font-semibold hover:underline\">\n          CovaSeal 20\n        </a>\n        {' '}and{' '}\n        <a href=\"/en/solutions/covametal-20\" className=\"text-primary font-semibold hover:underline\">\n          CovaMetal 20\n        </a>{' '}\n        are protected by an anti-UV top coat that lets them last two decades on a stable\n        opaque surface. On polycarbonate, the substrate itself ages under the effect of\n        UV, even when protected by a solar-control coating. After 5 to 8 years, the\n        degradation of the underlying polycarbonate requires a system refurbishment or\n        skylight replacement. An annual rinse with clean water extends the reflective\n        performance during this period.\n      </>\n    ),\n  },\n  {\n    q: 'Do you need a professional to apply CovaTherm Light?',\n    text: 'Yes. Application on skylights presents three specific challenges: the surfaces are often sloped or curved, their load-bearing capacity is low (you do not walk directly on them, you work from gangways or aerial work platforms), and the dosage must be adjusted to preserve translucency without compromising reflectivity. We recommend entrusting the application to a professional applicator familiar with these specific constraints of translucent surfaces, and we provide your installer with detailed application instructions. This is a condition of the manufacturer warranty.',\n  },\n  {\n    q: 'What is the price of a UV-protective coating for polycarbonate per m²?',\n    text: 'The price of a professional UV-protective coating for polycarbonate, installed, is calculated by quote, based on three factors: the accessibility of the skylights (technical gangway, aerial work platform, lifeline), their geometry (flat, curved, vaulted) and their condition before application. Compared with a full skylight replacement that runs into hundreds of euros per m², CovaTherm Light remains a particularly cost-effective solution, completed in 1 to 3 days with no interruption of activity.',\n  },\n  {\n    q: 'What maintenance does a solar-control coating require?',\n    text: 'CovaTherm Light has self-cleaning properties. We recommend an annual visit and a rinse with water to clear away dust and optimize solar reflection. No chemical product is needed. A visual inspection of the polycarbonate substrate\\'s condition every 2 to 3 years allows early detection of any underlying degradation.',\n  },\n];\n\nconst faqsForRender = faqs.map(({ q, text, node }) => ({ q, a: node ?? text }));\n\n// ─── Schemas JSON-LD ────────────────────────────────────────────────────────\n\nconst SITE_URL = 'https://www.covalba.fr';\nconst PAGE_URL = `${SITE_URL}/en/solutions/covatherm-light`;\nconst IMAGE_URL = `${SITE_URL}/covatherm-light.webp`;\n\nconst productSchema = {\n  '@context': 'https://schema.org/',\n  '@type': 'Product',\n  name: 'UV-protective coating for polycarbonate CovaTherm Light',\n  image: IMAGE_URL,\n  description:\n    'Non-opacifying solar-control coating for polycarbonate and polyester skylights and barrel-vault rooflights. Filters UV and infrared, preserves 65% of natural light. Reflective coating from Covalba.',\n  brand: { '@type': 'Brand', name: 'Covalba' },\n  category: 'UV-protective coating for polycarbonate',\n};\n\nconst breadcrumbSchema = {\n  '@context': 'https://schema.org',\n  '@type': 'BreadcrumbList',","is_error":false}]},"uuid":"fb73f098-74e7-475a-9ce1-98a5f2ebc168","timestamp":"2026-06-22T00:03:44.011Z","toolUseResult":{"stdout":"  },\n  {\n    q: 'From what proportion of skylights should the roof be treated?',\n    text: 'The more roof surface your skylights occupy, the greater the measurable impact of solar-control treatment on the interior atmosphere. A building with 25% or more of its roof in translucent elements achieves very visible comfort gains after applying CovaTherm Light. Below 20%, it is often more relevant to start by treating the opaque part of the roof with CovaTherm, then complement it with CovaTherm Light if the need remains.',\n  },\n  {\n    q: 'Can CovaTherm Light be applied on glazing or a glass roof?',\n    text: 'No. For glass (windows, glass roofs, sunrooms), the aesthetic finish of a solar-control coating applied by brush or sprayer is not satisfactory, and durability over time is lower. For these substrates, a bonded interior solar film remains the recommended solution. CovaTherm Light is specifically designed for translucent polymers (polycarbonate, polyester), which make up the vast majority of skylights and barrel-vault rooflights in industrial and commercial buildings.',\n  },\n  {\n    q: 'Why does CovaTherm Light last 5 to 8 years and not 20 like the other Covalba products?',\n    text: 'The substrate determines the lifespan of a coating. CovaTherm 20, CovaSeal 20 and CovaMetal 20 are protected by an anti-UV top coat that lets them last two decades on a stable opaque surface. On polycarbonate, the substrate itself ages under the effect of UV, even when protected by a solar-control coating. After 5 to 8 years, the degradation of the underlying polycarbonate requires a system refurbishment or skylight replacement. An annual rinse with clean water extends the reflective performance during this period.',\n    node: (\n      <>\n        The substrate determines the lifespan of a coating.{' '}\n        <a href=\"/en/solutions/covatherm\" className=\"text-primary font-semibold hover:underline\">\n          CovaTherm 20\n        </a>\n        ,{' '}\n        <a href=\"/en/solutions/covaseal-20\" className=\"text-primary font-semibold hover:underline\">\n          CovaSeal 20\n        </a>\n        {' '}and{' '}\n        <a href=\"/en/solutions/covametal-20\" className=\"text-primary font-semibold hover:underline\">\n          CovaMetal 20\n        </a>{' '}\n        are protected by an anti-UV top coat that lets them last two decades on a stable\n        opaque surface. On polycarbonate, the substrate itself ages under the effect of\n        UV, even when protected by a solar-control coating. After 5 to 8 years, the\n        degradation of the underlying polycarbonate requires a system refurbishment or\n        skylight replacement. An annual rinse with clean water extends the reflective\n        performance during this period.\n      </>\n    ),\n  },\n  {\n    q: 'Do you need a professional to apply CovaTherm Light?',\n    text: 'Yes. Application on skylights presents three specific challenges: the surfaces are often sloped or curved, their load-bearing capacity is low (you do not walk directly on them, you work from gangways or aerial work platforms), and the dosage must be adjusted to preserve translucency without compromising reflectivity. We recommend entrusting the application to a professional applicator familiar with these specific constraints of translucent surfaces, and we provide your installer with detailed application instructions. This is a condition of the manufacturer warranty.',\n  },\n  {\n    q: 'What is the price of a UV-protective coating for polycarbonate per m²?',\n    text: 'The price of a professional UV-protective coating for polycarbonate, installed, is calculated by quote, based on three factors: the accessibility of the skylights (technical gangway, aerial work platform, lifeline), their geometry (flat, curved, vaulted) and their condition before application. Compared with a full skylight replacement that runs into hundreds of euros per m², CovaTherm Light remains a particularly cost-effective solution, completed in 1 to 3 days with no interruption of activity.',\n  },\n  {\n    q: 'What maintenance does a solar-control coating require?',\n    text: 'CovaTherm Light has self-cleaning properties. We recommend an annual visit and a rinse with water to clear away dust and optimize solar reflection. No chemical product is needed. A visual inspection of the polycarbonate substrate\\'s condition every 2 to 3 years allows early detection of any underlying degradation.',\n  },\n];\n\nconst faqsForRender = faqs.map(({ q, text, node }) => ({ q, a: node ?? text }));\n\n// ─── Schemas JSON-LD ────────────────────────────────────────────────────────\n\nconst SITE_URL = 'https://www.covalba.fr';\nconst PAGE_URL = `${SITE_URL}/en/solutions/covatherm-light`;\nconst IMAGE_URL = `${SITE_URL}/covatherm-light.webp`;\n\nconst productSchema = {\n  '@context': 'https://schema.org/',\n  '@type': 'Product',\n  name: 'UV-protective coating for polycarbonate CovaTherm Light',\n  image: IMAGE_URL,\n  description:\n    'Non-opacifying solar-control coating for polycarbonate and polyester skylights and barrel-vault rooflights. Filters UV and infrared, preserves 65% of natural light. Reflective coating from Covalba.',\n  brand: { '@type': 'Brand', name: 'Covalba' },\n  category: 'UV-protective coating for polycarbonate',\n};\n\nconst breadcrumbSchema = {\n  '@context': 'https://schema.org',\n  '@type': 'BreadcrumbList',","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"1e01ecba-3c4a-498f-a83d-eb970cb8d460","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"fb73f098-74e7-475a-9ce1-98a5f2ebc168","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01JKLG4VHSkMAcG2JvsTev1c","type":"message","role":"assistant","content":[{"type":"text","text":"I have everything I need. Now I'll create the EN template. The translations preserve all metric units, structure, classes, and logic. Key decisions:\n- `/diagnostic` → `/en` (no EN contact/diagnostic route exists)\n- breadcrumb \"Accueil\" → \"Home\", links `/` → `/en`, Toitures → \"Roofs\"\n- Currency formatting: keep `formatCurrency`/`Intl` but switch locale to `en-US` and put `€` symbol after with a space (metric kept). Actually EN page data is English but values/units stay metric. I'll format numbers with `en-US` grouping.\n- `displayCell` replaces `EUR`→`€`, `m2`→`m²` — keep as-is.\n- The `getTreatmentRange`, `getProblemTitle` use French keyword matching on `page.*` data. Since EN page data will be English, those French keyword maps won't match. I'll translate the heuristic keywords to English equivalents AND keep slug-based matching (slugs are language-neutral). `getProblemTitle` maps full French titles → won't match EN titles, so it falls back to `{ strong: title, muted: '' }` which renders the full EN title cleanly. That's acceptable; I'll flag it."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":2318,"cache_read_input_tokens":94798,"cache_creation":{"ephemeral_5m_input_tokens":2318,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKUANvyqMwE8DdaLxUc","attributionAgent":"general-purpose","type":"assistant","uuid":"657d2b0e-e2c8-45a2-a2f6-81197e871421","timestamp":"2026-06-22T00:03:52.065Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"657d2b0e-e2c8-45a2-a2f6-81197e871421","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01JKLG4VHSkMAcG2JvsTev1c","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019pZwvZ5Zr41esg8TYDLf12","name":"Bash","input":{"command":"cd /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all\ngrep -n \"maxime-bourassin\\|maxime\\.jpg\\|/logos/\\|/images/toitures/\" public/* -r 2>/dev/null | head; ls public/images/toitures/ 2>/dev/null | head; echo \"---\"; ls public/ | grep -i maxime","description":"Verify shared public assets exist"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":2318,"cache_read_input_tokens":94798,"output_tokens":621,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":2318},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":621,"cache_read_input_tokens":94798,"cache_creation_input_tokens":2318,"cache_creation":{"ephemeral_5m_input_tokens":2318,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CcHKUANvyqMwE8DdaLxUc","attributionAgent":"general-purpose","type":"assistant","uuid":"38a8a57d-1f3b-4e8c-bcd1-b2d397698e4c","timestamp":"2026-06-22T00:03:54.606Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"38a8a57d-1f3b-4e8c-bcd1-b2d397698e4c","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_019pZwvZ5Zr41esg8TYDLf12","type":"tool_result","content":"benefit-chantier-evite.webp\nbenefit-clim.webp\nbenefit-confort.webp\nbenefit-protection-toiture.webp\nfeedback-batiment-tempere.png\nfeedback-economie-clim-1.png\nfeedback-economie-clim-2.png\nfeedback-economie-clim-3.png\nfeedback-etancheite.png\nsupport-bac-acier-chaud.webp\n---\nmaxime-bourassin.jpg","is_error":false}]},"uuid":"7be9a0a3-76d3-4d4c-a61c-181fe9c76b13","timestamp":"2026-06-22T00:03:54.701Z","toolUseResult":{"stdout":"benefit-chantier-evite.webp\nbenefit-clim.webp\nbenefit-confort.webp\nbenefit-protection-toiture.webp\nfeedback-batiment-tempere.png\nfeedback-economie-clim-1.png\nfeedback-economie-clim-2.png\nfeedback-economie-clim-3.png\nfeedback-etancheite.png\nsupport-bac-acier-chaud.webp\n---\nmaxime-bourassin.jpg","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"38a8a57d-1f3b-4e8c-bcd1-b2d397698e4c","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"7be9a0a3-76d3-4d4c-a61c-181fe9c76b13","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01TcGZdQa6q4s1yfDQzMTkb7","type":"message","role":"assistant","content":[{"type":"text","text":"Assets confirmed shared. Now I'll create the EN template file."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":131,"cache_creation_input_tokens":770,"cache_read_input_tokens":97116,"cache_creation":{"ephemeral_5m_input_tokens":770,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKUx6GwCqRoUoUWMbMy","attributionAgent":"general-purpose","type":"assistant","uuid":"b4b6c3bc-ccb4-4265-9f45-7390dfe7eb33","timestamp":"2026-06-22T00:03:56.589Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"b4b6c3bc-ccb4-4265-9f45-7390dfe7eb33","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_01TcGZdQa6q4s1yfDQzMTkb7","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_012sXozubke9NR11oHBGjvSS","name":"Write","input":{"file_path":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/en/roof/RoofPageTemplateEN.tsx","content":"import { useState } from 'react';\nimport {\n  ArrowRight,\n  BadgeCheck,\n  CalendarClock,\n  CalendarCheck,\n  Check,\n  CheckCircle2,\n  ChevronDown,\n  Factory,\n  HelpCircle,\n  Play,\n  ShieldCheck,\n  ShieldAlert,\n  Sparkles,\n  SunMedium,\n  Thermometer,\n  TrendingDown,\n  Users,\n  Wrench,\n  ClipboardCheck,\n  type LucideIcon,\n} from 'lucide-react';\n\nimport FooterEN from '@/components/en/FooterEN';\nimport NavbarEN from '@/components/en/NavbarEN';\nimport ScrollReveal from '@/components/ScrollReveal';\nimport StickyMobileCTAEN from '@/components/en/StickyMobileCTAEN';\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';\nimport { Dialog, DialogContent } from '@/components/ui/dialog';\nimport { useJsonLd, usePageMeta } from '@/hooks/usePageMeta';\nimport { type RoofPageData, type TextCard } from '@/data/roofPages';\nimport coolRoofLayers from '@/assets/cool-roof-layers.webp';\nimport covaMetalProduct from '@/assets/produits/Covalba-CovaMetal20-Duo-Produit.webp';\nimport covaSealProduct from '@/assets/produits/Covalba-CovaSeal20-Duo-Produit.webp';\nimport covaTherm8Product from '@/assets/produits/Covalba-CovaTherm8-Produit.webp';\nimport covaTherm20Product from '@/assets/produits/Covalba-CovaTherm20-Produit.webp';\n\ninterface RoofPageTemplateENProps {\n  page: RoofPageData;\n}\n\nconst SITE_URL = 'https://www.covalba.fr';\n\n// Internal route for all CTAs on the EN site. There is no dedicated EN\n// diagnostic/contact route yet, so every conversion CTA points to the EN home.\nconst CTA_HREF = '/en';\n\nconst cardIcons: LucideIcon[] = [SunMedium, Wrench, Factory, ShieldCheck];\nconst methodIcons: LucideIcon[] = [ClipboardCheck, CalendarClock, ShieldCheck, TrendingDown];\nconst proofIcons: LucideIcon[] = [Thermometer, ShieldCheck, BadgeCheck];\nconst problemIcons: LucideIcon[] = [Users, SunMedium, ShieldAlert];\n\nconst logos = [\n  { name: 'Continental', url: '/logos/logo-continental.svg' },\n  { name: 'Thales', url: '/logos/logo-thales.svg' },\n  { name: 'Nestlé', url: '/logos/logo-nestle.svg' },\n  { name: 'SNCF', url: '/logos/logo-sncf.svg' },\n  { name: 'Leclerc', url: '/logos/logo-leclerc.svg' },\n  { name: 'Super U', url: '/logos/logo-super-u.svg' },\n];\n\nconst displayCell = (value: string) => value.replace(/EUR/g, '€').replace(/m2/g, 'm²');\n\nconst formatCurrency = (value: number) =>\n  `${new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(Math.round(value))} €`;\n\nconst parsePricePerM2 = (value: string) => {\n  const match = displayCell(value).match(/\\(([\\d,.]+)\\s*€\\/m²\\)/);\n  if (!match) return null;\n\n  return Number.parseFloat(match[1].replace(',', '.'));\n};\n\nconst getTreatmentRange = (page: RoofPageData, situation: string) => {\n  const text = `${page.slug} ${situation}`.toLowerCase();\n\n  if (\n    text.includes('covaseal') ||\n    text.includes('membrane-bitumineuse') ||\n    text.includes('toiture-plate') ||\n    text.includes('aging') ||\n    text.includes('leak')\n  ) {\n    return { min: 28, max: 38, label: 'CovaSeal 20 estimate' };\n  }\n\n  if (\n    text.includes('covametal') ||\n    text.includes('bac-acier') ||\n    text.includes('corrosion') ||\n    text.includes('steel')\n  ) {\n    return { min: 24, max: 32, label: 'CovaMetal 20 estimate' };\n  }\n\n  if (text.includes('fibrociment') || text.includes('fiber') || text.includes('sheets')) {\n    return { min: 22, max: 28, label: 'CovaTherm sensitive substrate' };\n  }\n\n  return { min: 20, max: 25, label: 'CovaTherm estimate' };\n};\n\nconst getFallbackPricePerM2 = (option: string, optionIndex: number, treatmentMidpoint: number) => {\n  const lower = option.toLowerCase();\n\n  if (optionIndex === 0) return treatmentMidpoint;\n  if (lower.includes('anticorrosion') || lower.includes('repaint')) return 16;\n  if (lower.includes('replacement') || lower.includes('replace')) return 120;\n  if (lower.includes('new membrane') || lower.includes('membrane')) return 90;\n  if (lower.includes('insulation')) return 150;\n\n  return null;\n};\n\nconst benefitImages = [\n  '/images/toitures/benefit-confort.webp',\n  '/images/toitures/benefit-protection-toiture.webp',\n  '/images/toitures/benefit-clim.webp',\n  '/images/toitures/benefit-chantier-evite.webp',\n];\n\nconst feedbackRoofBenefitImages = [\n  '/images/toitures/feedback-batiment-tempere.png',\n  '/images/toitures/feedback-etancheite.png',\n  '/images/toitures/feedback-economie-clim-1.png',\n  '/images/toitures/feedback-economie-clim-3.png',\n];\n\nconst getBenefitImage = (page: RoofPageData, benefit: TextCard, index: number) => {\n  if (['membrane-bitumineuse', 'toiture-plate'].includes(page.slug)) {\n    return feedbackRoofBenefitImages[index] ?? feedbackRoofBenefitImages[0];\n  }\n\n  const text = `${benefit.title} ${benefit.text}`.toLowerCase();\n\n  if (page.slug === 'bac-acier' && (text.includes('steel') || text.includes('corrosion') || text.includes('rust'))) {\n    return '/images/toitures/support-bac-acier-cool.webp';\n  }\n\n  if (text.includes('bill') || text.includes('cooling') || text.includes('air conditioning') || text.includes('cold')) {\n    return '/images/toitures/benefit-clim.webp';\n  }\n\n  if (\n    text.includes('project') ||\n    text.includes('refurbishment') ||\n    text.includes('replacement') ||\n    text.includes('removal') ||\n    text.includes('strip')\n  ) {\n    return '/images/toitures/benefit-chantier-evite.webp';\n  }\n\n  if (\n    text.includes('protect') ||\n    text.includes('waterproof') ||\n    text.includes('corrosion') ||\n    text.includes('lifespan') ||\n    text.includes('durab')\n  ) {\n    return '/images/toitures/benefit-protection-toiture.webp';\n  }\n\n  return benefitImages[index] ?? benefitImages[0];\n};\n\nconst supportVisuals: Record<string, { hot: string; cool: string; hotLabel: string; coolLabel: string }> = {\n  'membrane-bitumineuse': {\n    hot: '/images/toitures/support-bitume-chaud.webp',\n    cool: '/images/toitures/feedback-economie-clim-2.png',\n    hotLabel: 'Without cool roof: bitumen membrane absorbing heat',\n    coolLabel: 'With cool roof: reflective membrane',\n  },\n  'bac-acier': {\n    hot: '/images/toitures/support-bac-acier-chaud.webp',\n    cool: '/images/toitures/support-bac-acier-cool.webp',\n    hotLabel: 'Without cool roof: overheating metal',\n    coolLabel: 'With cool roof: reflective, protected steel deck',\n  },\n  'toiture-plate': {\n    hot: '/images/toitures/support-toiture-plate-chaud.webp',\n    cool: '/images/toitures/feedback-economie-clim-2.png',\n    hotLabel: 'Without cool roof: roof terrace absorbing heat',\n    coolLabel: 'With cool roof: reflective roof terrace',\n  },\n  fibrociment: {\n    hot: '/images/toitures/support-fibrociment-chaud.webp',\n    cool: '/images/toitures/support-fibrociment-cool.webp',\n    hotLabel: 'Without cool roof: fiber cement absorbing heat',\n    coolLabel: 'With cool roof: protected sheets',\n  },\n  'tuiles-ciment': {\n    hot: '/images/toitures/support-tuiles-ciment-chaud.webp',\n    cool: '/images/toitures/support-tuiles-ciment-cool.webp',\n    hotLabel: 'Without cool roof: tiles storing heat',\n    coolLabel: 'With cool roof: reflective tiles',\n  },\n};\n\nconst layerTitles = ['Bonding primer', 'Reflective resin', 'Protective top coat'];\n\nconst layerColors = [\n  {\n    pill: 'bg-terracotta text-white',\n    dot: 'bg-terracotta',\n    chevron: 'text-terracotta',\n  },\n  {\n    pill: 'bg-foreground text-white',\n    dot: 'bg-foreground',\n    chevron: 'text-foreground',\n  },\n  {\n    pill: 'bg-primary text-white',\n    dot: 'bg-primary',\n    chevron: 'text-primary',\n  },\n];\n\nconst layerPositions = [\n  { top: '56.7%', left: '36.5%' },\n  { top: '48.2%', left: '36.5%' },\n  { top: '36.3%', left: '35.6%' },\n];\n\nconst getProblemTitle = (title: string) => {\n  const map: Record<string, { strong: string; muted: string }> = {\n    'A black bitumen roof is a radiator sitting on top of your building.': {\n      strong: 'Your roof heats up.',\n      muted: 'Your building takes the hit.',\n    },\n    'Steel decking heats up fast, rusts fast, and wears out fast.': {\n      strong: 'The metal heats up fast.',\n      muted: 'The roof wears out too.',\n    },\n    'Flat, your roof takes the sun all day long.': {\n      strong: 'Your roof terrace takes it all.',\n      muted: 'Heat, UV, waterproofing.',\n    },\n    'Fiber cement heats up, ages, and is expensive to remove.': {\n      strong: 'Fiber cement heats up.',\n      muted: 'Removing it is expensive.',\n    },\n    'Dark cement tiles, overheating attic space.': {\n      strong: 'The tiles store the heat.',\n      muted: 'The attic space takes it.',\n    },\n  };\n\n  return map[title] ?? { strong: title, muted: '' };\n};\n\nconst getLayerDescription = (index: number) => {\n  if (index === 0) {\n    return 'Adapts to the existing substrate. Every roof has its own chemistry, and every chemistry calls for tailored preparation and bonding.';\n  }\n\n  if (index === 1) {\n    return 'This is the heart of the system. The surface reflects solar radiation instead of absorbing it, so less heat enters the building.';\n  }\n\n  return 'A top coat protects the resin from UV, pollution and premature aging to preserve performance over time.';\n};\n\nconst getLayerExpert = (index: number) => {\n  const notes = [\n    'An unsuitable primer means a system that blisters and peels off. The substrate decides everything.',\n    'Cool roof treats the cause: the roof heats up less, so the building takes on less heat.',\n    'The difference between a paint job and an investment is how it holds up over time. The finish is there to protect performance.',\n  ];\n\n  return notes[index] ?? notes[0];\n};\n\nconst Label = ({ children, light = false }: { children: string; light?: boolean }) => (\n  <div className=\"mb-5 flex items-center gap-3\">\n    <span className={`h-px w-9 ${light ? 'bg-teal-vivid/70' : 'bg-primary/65'}`} aria-hidden=\"true\" />\n    <span\n      className={`font-body text-[11px] font-bold uppercase tracking-[0.22em] ${\n        light ? 'text-teal-vivid' : 'text-primary'\n      }`}\n    >\n      {children}\n    </span>\n  </div>\n);\n\nconst PrimaryCTA = ({ label, href }: { label: string; href: string }) => (\n  <a\n    href={href}\n    className=\"group inline-flex items-center justify-center gap-2 rounded-full cta-gradient py-2.5 pl-6 pr-2 font-semibold text-white\"\n  >\n    <span className=\"text-sm\">{label}</span>\n    <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n      <ArrowRight className=\"h-3.5 w-3.5\" />\n    </span>\n  </a>\n);\n\nconst RoofHero = ({ page }: { page: RoofPageData }) => {\n  const [form, setForm] = useState({ climate: '', surface: '', email: '' });\n  const [submitted, setSubmitted] = useState(false);\n  const surfaceNum = parseFloat(form.surface) || 0;\n  const estimate = surfaceNum ? `${new Intl.NumberFormat('en-US').format(surfaceNum * 22.5)} €` : '20-25 €/m²';\n\n  return (\n    <section\n      aria-labelledby={`${page.slug}-hero-title`}\n      className=\"relative isolate flex min-h-[100svh] flex-col overflow-hidden bg-navy text-cream\"\n    >\n      <img\n        src={page.heroImage}\n        alt={page.heroImageAlt}\n        className=\"absolute inset-0 -z-10 size-full scale-[1.02] object-cover\"\n        style={page.slug === 'membrane-bitumineuse' ? { objectPosition: 'center 28%' } : undefined}\n        loading=\"eager\"\n        decoding=\"async\"\n      />\n      <div\n        aria-hidden=\"true\"\n        className=\"absolute inset-0 -z-10 hidden bg-gradient-to-r from-navy/[0.94] via-navy/62 to-navy/25 lg:block\"\n      />\n      <div\n        aria-hidden=\"true\"\n        className={`absolute inset-0 -z-10 bg-gradient-to-b lg:hidden ${\n          page.slug === 'membrane-bitumineuse'\n            ? 'from-navy/92 via-navy/58 to-navy/96'\n            : 'from-navy/84 via-navy/38 to-navy/96'\n        }`}\n      />\n      <div aria-hidden=\"true\" className=\"absolute inset-x-0 bottom-0 -z-10 h-[28%] bg-gradient-to-t from-navy via-navy/85 to-transparent\" />\n\n      <div className=\"container relative z-10 mx-auto flex flex-1 items-center px-6 pb-10 pt-24 lg:px-12 lg:pb-16 lg:pt-28\">\n        <div className=\"grid w-full grid-cols-1 items-center gap-10 lg:grid-cols-[55fr_45fr] lg:gap-12\">\n          <div className=\"max-w-2xl space-y-6 lg:space-y-8\">\n            <ScrollReveal>\n              <nav className=\"flex items-center gap-2 font-body text-xs text-cream/50\">\n                <a href=\"/en\" className=\"transition-colors hover:text-cream/75\">Home</a>\n                <span className=\"text-cream/28\">/</span>\n                <span>Roofs</span>\n                <span className=\"text-cream/28\">/</span>\n                <span className=\"text-cream/78\">{page.breadcrumb}</span>\n              </nav>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <h1\n                id={`${page.slug}-hero-title`}\n                className=\"font-satoshi font-bold leading-[1.02] text-white\"\n                style={{ fontSize: 'clamp(2.25rem, 4.6vw, 4rem)', letterSpacing: '-0.03em' }}\n              >\n                {page.h1}\n              </h1>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <p\n                className=\"max-w-xl font-satoshi font-medium leading-[1.3] text-cream/66\"\n                style={{ fontSize: 'clamp(1rem, 1.5vw, 1.375rem)', letterSpacing: '-0.005em' }}\n              >\n                {page.lead}\n              </p>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <ul className=\"hidden gap-4 pt-1 sm:flex sm:flex-wrap lg:gap-6\" aria-label=\"Key benefits\">\n                {page.heroStats.map((stat, index) => {\n                  const Icon = [Thermometer, ShieldCheck, TrendingDown][index] ?? BadgeCheck;\n                  return (\n                    <li\n                      key={stat.label}\n                      className={`flex items-start gap-3 ${index !== 0 ? 'sm:border-l sm:border-white/10 sm:pl-6' : ''}`}\n                    >\n                      <Icon className=\"mt-0.5 h-[18px] w-[18px] shrink-0 text-teal-vivid\" strokeWidth={1.5} />\n                      <div className=\"leading-tight\">\n                        <p className=\"font-satoshi text-[15px] font-bold text-white lg:text-base\">{stat.value}</p>\n                        <p className=\"mt-1 font-body text-[12.5px] text-cream/55 lg:text-[13px]\">{stat.label}</p>\n                      </div>\n                    </li>\n                  );\n                })}\n              </ul>\n            </ScrollReveal>\n          </div>\n\n          <ScrollReveal>\n            <div className=\"rounded-[1.5rem] border border-white/[0.08] bg-white/[0.04] p-1.5 backdrop-blur-sm\">\n              <div className=\"rounded-[calc(1.5rem-6px)] border border-white/[0.06] bg-[#0b1a2b]/80 p-4 shadow-[inset_0_1px_1px_rgba(255,255,255,0.06)] backdrop-blur-xl lg:p-7\">\n                {!submitted ? (\n                  <>\n                    <h2 className=\"mb-1 text-center font-satoshi text-xl font-bold text-white lg:text-2xl\">\n                      Estimate your potential savings\n                    </h2>\n                    <p className=\"mb-4 text-center font-body text-[12px] text-white/50\">\n                      Tailored reply within 24h\n                    </p>\n                    <form\n                      onSubmit={(event) => {\n                        event.preventDefault();\n                        setSubmitted(true);\n                      }}\n                      className=\"space-y-2.5 lg:space-y-3\"\n                    >\n                      <input\n                        value={page.breadcrumb}\n                        readOnly\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white outline-none\"\n                      />\n                      <select\n                        required\n                        value={form.climate}\n                        onChange={(e) => setForm({ ...form, climate: e.target.value })}\n                        className=\"w-full appearance-none rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      >\n                        <option value=\"\" disabled className=\"bg-card text-foreground\">Is the building air-conditioned?</option>\n                        {['Yes', 'No', 'Partially', 'To confirm'].map((option) => (\n                          <option key={option} value={option} className=\"bg-card text-foreground\">{option}</option>\n                        ))}\n                      </select>\n                      <input\n                        type=\"number\"\n                        placeholder=\"Roof surface (m²)\"\n                        required\n                        min=\"1\"\n                        value={form.surface}\n                        onChange={(e) => setForm({ ...form, surface: e.target.value })}\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      />\n                      <input\n                        type=\"email\"\n                        placeholder=\"Work email\"\n                        required\n                        value={form.email}\n                        onChange={(e) => setForm({ ...form, email: e.target.value })}\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      />\n\n                      <button\n                        type=\"submit\"\n                        className=\"group mt-1 flex w-full items-center justify-center gap-3 rounded-full cta-gradient py-3 font-semibold text-white active:scale-[0.98]\"\n                      >\n                        <span className=\"text-[15px]\">See my potential</span>\n                        <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n                          <ArrowRight className=\"h-4 w-4\" />\n                        </span>\n                      </button>\n                    </form>\n\n                    <p className=\"mt-3 text-center font-body text-[11px] text-white/35\">\n                      No commitment · Study tailored to your substrate\n                    </p>\n                  </>\n                ) : (\n                  <div className=\"py-4 text-center\">\n                    <div className=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary/20\">\n                      <ShieldCheck className=\"h-6 w-6 text-primary\" />\n                    </div>\n                    <p className=\"mb-1 font-body text-sm text-white/60\">Indicative basis for {new Intl.NumberFormat('en-US').format(surfaceNum)} m²</p>\n                    <p className=\"mb-4 font-satoshi text-2xl font-bold text-white\">{estimate}</p>\n                    <p className=\"mb-4 font-body text-[13px] text-white/50\">\n                      An advisor refines the estimate based on the actual condition of the roof.\n                    </p>\n                    <button onClick={() => setSubmitted(false)} className=\"font-body text-sm font-semibold text-primary hover:underline\">\n                      Edit my request\n                    </button>\n                  </div>\n                )}\n              </div>\n            </div>\n          </ScrollReveal>\n        </div>\n      </div>\n\n      <div className=\"relative z-10 hidden border-t border-white/10 bg-navy/78 backdrop-blur-md lg:block\">\n        <div className=\"container mx-auto flex items-center gap-8 px-12 py-5\">\n          <p className=\"shrink-0 font-body text-[10px] font-semibold uppercase tracking-[0.24em] text-cream/45\">\n            They trust us\n          </p>\n          <div className=\"flex flex-1 items-center justify-between gap-8\">\n            {logos.map((logo) => (\n              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n                <img\n                  src={logo.url}\n                  alt={logo.name}\n                  className=\"max-h-7 max-w-full object-contain opacity-65 brightness-0 invert transition-opacity duration-300 hover:opacity-95\"\n                  loading=\"eager\"\n                  decoding=\"async\"\n                />\n              </span>\n            ))}\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst ProblemSection = ({ page }: { page: RoofPageData }) => {\n  const title = getProblemTitle(page.problem.title);\n  const visual = supportVisuals[page.slug] ?? supportVisuals['membrane-bitumineuse'];\n\n  return (\n    <section className=\"relative overflow-hidden bg-cream py-16 lg:py-36\">\n      <div className=\"container relative mx-auto px-4 lg:px-8\">\n        <ScrollReveal>\n          <div className=\"mx-auto mb-14 max-w-3xl text-center lg:mb-20\">\n            <h2\n              className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n              style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n            >\n              {title.strong}\n              {title.muted && <span className=\"text-foreground/30\"> {title.muted}</span>}\n            </h2>\n          </div>\n        </ScrollReveal>\n\n        <div className=\"grid grid-cols-1 items-center gap-12 lg:grid-cols-12 lg:gap-16\">\n          <div className=\"order-2 lg:order-1 lg:col-span-5\">\n            <ScrollReveal>\n              <div className=\"relative\">\n                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_hsl(var(--terracotta)/0.18)_0%,_transparent_65%)] blur-2xl\" />\n                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_hsl(var(--terracotta-dark)/0.25),_0_10px_30px_-5px_hsl(var(--terracotta)/0.12)]\">\n                  <img\n                    src={visual.hot}\n                    alt=\"\"\n                    className=\"h-full w-full object-contain p-4\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                </div>\n              </div>\n              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-terracotta/60\">\n                {visual.hotLabel}\n              </p>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"order-1 lg:order-2 lg:col-span-7 lg:pl-12\">\n            <ScrollReveal stagger>\n              <div\n                className=\"space-y-8 border-l-2 pl-8 lg:space-y-10 lg:pl-10\"\n                style={{ borderImage: 'linear-gradient(to bottom, hsl(var(--terracotta)), hsl(var(--terracotta-dark))) 1' }}\n              >\n                {page.problem.cards.map((card, index) => {\n                  const Icon = problemIcons[index] ?? HelpCircle;\n                  return (\n                    <div key={card.title}>\n                      <div className=\"mb-2 flex items-center gap-3\">\n                        <Icon className=\"h-5 w-5 text-terracotta\" strokeWidth={1.8} />\n                        <h3\n                          className=\"font-satoshi text-xl font-bold text-foreground lg:text-2xl\"\n                          style={{ letterSpacing: '-0.02em', lineHeight: 1.2 }}\n                        >\n                          {card.title}\n                        </h3>\n                      </div>\n                      <p className=\"font-body text-base leading-relaxed text-foreground/55\">{card.text}</p>\n                    </div>\n                  );\n                })}\n              </div>\n            </ScrollReveal>\n          </div>\n        </div>\n\n        <div className=\"mt-20 grid grid-cols-1 items-center gap-12 lg:mt-28 lg:grid-cols-12 lg:gap-16\">\n          <div className=\"order-2 lg:order-2 lg:col-span-5\">\n            <ScrollReveal>\n              <div className=\"relative\">\n                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_rgba(32,150,140,0.18)_0%,_transparent_65%)] blur-2xl\" />\n                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_rgba(20,120,110,0.20),_0_10px_30px_-5px_rgba(32,150,140,0.10)]\">\n                  <img\n                    src={visual.cool}\n                    alt=\"\"\n                    className=\"h-full w-full object-contain p-4\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                </div>\n              </div>\n              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-primary/60\">\n                {visual.coolLabel}\n              </p>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"order-1 lg:order-1 lg:col-span-7 lg:pr-12\">\n            <ScrollReveal>\n              <h3\n                className=\"mb-8 max-w-xl font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                Cool roof: a concrete answer\n                <span className=\"text-foreground/30\"> to the heat problem.</span>\n              </h3>\n              <p className=\"mb-8 max-w-lg font-body text-base leading-relaxed text-foreground/50\">\n                A light-colored surface sends solar radiation back before it penetrates the building. The roof heats up less, the top floors can breathe, and the substrate is subjected to less UV and thermal shock.\n              </p>\n              <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n            </ScrollReveal>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst SolutionSection = ({ page }: { page: RoofPageData }) => {\n  const [active, setActive] = useState(0);\n\n  return (\n    <section className=\"relative bg-white\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n        <ScrollReveal>\n          <div className=\"flex flex-col gap-8 pb-10 pt-16 lg:flex-row lg:items-end lg:gap-16 lg:pb-16 lg:pt-32\">\n            <div className=\"lg:w-2/3\">\n              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-muted-foreground\">\n                {page.solution.badge}\n              </p>\n              <h2\n                className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                <span className=\"text-foreground/30\">A system designed to </span>\n                reflect, protect and last.\n              </h2>\n            </div>\n            <div className=\"lg:w-1/3 lg:pb-1\">\n              <p className=\"font-body text-base leading-relaxed text-foreground/52 lg:text-right lg:text-lg\">\n                A primer, a resin and a top coat that guarantee your roof's performance over time.\n              </p>\n              <div className=\"mt-5 flex lg:justify-end\">\n                <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n              </div>\n            </div>\n          </div>\n        </ScrollReveal>\n\n        <div className=\"flex flex-col gap-12 pb-24 lg:flex-row lg:gap-16 lg:pb-32\">\n          <div className=\"overflow-hidden rounded-2xl shadow-[0_20px_60px_-15px_rgba(0,0,0,0.08)] lg:hidden\">\n            <img src={coolRoofLayers} alt=\"Cross-section of the Covalba cool roof system\" className=\"w-full\" loading=\"lazy\" />\n          </div>\n\n          <div className=\"lg:w-1/2 lg:sticky lg:top-[20vh] lg:self-start\">\n            {layerTitles.map((item, index) => {\n              const isOpen = active === index;\n              const colors = layerColors[index] ?? layerColors[0];\n              return (\n                <div key={item} className=\"border-b border-border last:border-b-0\">\n                  <button\n                    onClick={() => setActive(index)}\n                    className=\"group flex w-full items-center gap-4 py-6 text-left\"\n                  >\n                    <span\n                      className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full font-body text-[11px] font-bold transition-all duration-300 ${\n                        isOpen ? colors.pill : 'bg-muted text-muted-foreground'\n                      }`}\n                    >\n                      0{index + 1}\n                    </span>\n                    <h3\n                      className={`flex-1 font-satoshi text-xl font-bold transition-colors duration-300 lg:text-2xl ${\n                        isOpen ? 'text-foreground' : 'text-foreground/50 group-hover:text-foreground/80'\n                      }`}\n                    >\n                      {item}\n                    </h3>\n                    <ChevronDown\n                      className={`h-5 w-5 shrink-0 transition-all duration-300 ${\n                        isOpen ? `${colors.chevron} rotate-180` : 'text-foreground/25'\n                      }`}\n                      strokeWidth={2}\n                    />\n                  </button>\n\n                  <div\n                    className={`grid transition-all duration-500 ${\n                      isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'\n                    }`}\n                  >\n                    <div className=\"overflow-hidden\">\n                      <div className=\"pb-8 pl-6 lg:pl-12\">\n                        <p className=\"mb-6 font-body text-base leading-relaxed text-foreground/65 lg:text-lg\">\n                          {getLayerDescription(index)}\n                        </p>\n                        <div className=\"flex items-start gap-3 rounded-xl bg-foreground/[0.03] p-4\">\n                          <img\n                            src=\"/maxime-bourassin.jpg\"\n                            alt=\"Maxime Bourassin, founder of Covalba\"\n                            className=\"mt-0.5 h-9 w-9 shrink-0 rounded-full object-cover\"\n                          />\n                          <div>\n                            <p className=\"font-body text-[15px] italic leading-relaxed text-foreground/65 lg:text-base\">\n                              \"{getLayerExpert(index)}\"\n                            </p>\n                            <p className=\"mt-2 font-body text-xs font-semibold text-foreground/50\">\n                              Maxime Bourassin, founder\n                            </p>\n                          </div>\n                        </div>\n                        {index === 0 && page.solution.note && (\n                          <p className=\"mt-4 rounded-xl bg-primary/5 p-4 font-body text-sm leading-relaxed text-foreground/62\">\n                            {page.solution.note}\n                          </p>\n                        )}\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              );\n            })}\n          </div>\n\n          <div className=\"hidden lg:block lg:w-1/2\">\n            <div className=\"relative overflow-hidden rounded-2xl shadow-[0_30px_80px_-15px_rgba(0,0,0,0.08)]\">\n              <img src={coolRoofLayers} alt=\"Cross-section of the Covalba cool roof system\" className=\"w-full\" loading=\"lazy\" />\n              {layerTitles.map((item, index) => {\n                const pos = layerPositions[index];\n                const colors = layerColors[index] ?? layerColors[0];\n                const isActive = active === index;\n                if (!pos) return null;\n\n                return (\n                  <div key={item} className=\"absolute\" style={{ top: pos.top, left: pos.left }}>\n                    <div\n                      className={`absolute -translate-x-1/2 -translate-y-1/2 transition-all duration-500 ${\n                        isActive ? 'scale-125' : 'scale-75 opacity-40'\n                      }`}\n                    >\n                      <div\n                        className={`h-3.5 w-3.5 rounded-full ${\n                          isActive ? colors.dot : 'bg-foreground/20'\n                        } shadow-lg ring-2 ring-white transition-colors duration-500`}\n                      />\n                      {isActive && (\n                        <div className={`absolute inset-0 h-3.5 w-3.5 animate-ping rounded-full ${colors.dot} opacity-30`} />\n                      )}\n                    </div>\n                    <div\n                      className={`pointer-events-none absolute right-5 top-1/2 -translate-y-1/2 transition-all duration-500 ${\n                        isActive ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-0'\n                      }`}\n                    >\n                      <span className={`whitespace-nowrap rounded-full px-4 py-1.5 font-body text-[13px] font-bold shadow-lg ${colors.pill}`}>\n                        {item}\n                      </span>\n                    </div>\n                  </div>\n                );\n              })}\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst BenefitsSection = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-white py-20 lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mb-10 grid gap-8 lg:grid-cols-[0.82fr_1.18fr] lg:items-end lg:gap-14\">\n          <div>\n            <Label>{page.benefits.badge}</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n              {page.benefits.title}\n            </h2>\n          </div>\n          <p className=\"max-w-xl border-l border-primary/30 pl-6 font-body text-base leading-relaxed text-foreground/62 lg:text-lg\">\n            {page.benefits.featured}\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal stagger>\n        <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n          {page.benefits.cards.map((benefit, index) => {\n            const Icon = cardIcons[index] ?? Sparkles;\n            return (\n              <article\n                key={benefit.title}\n                className=\"group overflow-hidden rounded-2xl bg-cream ring-1 ring-foreground/[0.05] transition-all duration-500 hover:-translate-y-1 hover:shadow-[0_24px_70px_-48px_hsl(var(--navy)/0.45)]\"\n              >\n                <div className=\"relative aspect-[4/3] overflow-hidden bg-foreground/[0.04]\">\n                  <img\n                    src={getBenefitImage(page, benefit, index)}\n                    alt=\"\"\n                    className=\"h-full w-full object-cover transition-transform duration-700 group-hover:scale-[1.035]\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                  <div className=\"absolute inset-x-0 bottom-0 h-20 bg-gradient-to-t from-cream to-transparent\" />\n                </div>\n                <div className=\"p-5 lg:p-6\">\n                  <div className=\"mb-4 flex h-10 w-10 items-center justify-center rounded-xl bg-white text-primary shadow-[0_12px_30px_-24px_hsl(var(--navy)/0.35)]\">\n                    <Icon className=\"h-5 w-5\" strokeWidth={1.7} />\n                  </div>\n                  <h3 className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{benefit.title}</h3>\n                  <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/62\">{benefit.text}</p>\n                </div>\n              </article>\n            );\n          })}\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst RoiOptionCards = ({\n  page,\n  surface,\n  treatmentRange,\n}: {\n  page: RoofPageData;\n  surface: number;\n  treatmentRange: ReturnType<typeof getTreatmentRange>;\n}) => {\n  if (!page.roi.comparison) {\n    return page.roi.afterComparison ? (\n      <article className=\"rounded-2xl bg-white p-6 ring-1 ring-foreground/[0.06] lg:p-8\">\n        <p className=\"font-body text-base leading-relaxed text-foreground/66 lg:text-lg\">\n          {page.roi.afterComparison}\n        </p>\n      </article>\n    ) : null;\n  }\n\n  const options = page.roi.comparison.headers.slice(1);\n  const rows = page.roi.comparison.rows;\n  const investment = rows.find((row) => row[0].toLowerCase().includes('investment'));\n  const duration = rows.find((row) => {\n    const key = row[0].toLowerCase();\n    return key.includes('life') || key.includes('durat') || key.includes('lifespan');\n  });\n  const activity = rows.find((row) => {\n    const key = row[0].toLowerCase();\n    return key.includes('activity') || key.includes('operation');\n  });\n  const treatmentMidpoint = (treatmentRange.min + treatmentRange.max) / 2;\n\n  return (\n    <div className=\"grid gap-4 lg:grid-cols-3\">\n      {options.map((option, optionIndex) => {\n        const isCovalba = optionIndex === 0;\n        const rawInvestment = investment?.[optionIndex + 1] ?? '';\n        const parsedPrice = parsePricePerM2(rawInvestment);\n        const pricePerM2 = parsedPrice ?? getFallbackPricePerM2(option, optionIndex, treatmentMidpoint);\n        const dynamicInvestment = pricePerM2 ? `~${formatCurrency(surface * pricePerM2)}` : displayCell(rawInvestment);\n\n        return (\n          <article\n            key={option}\n            className={`rounded-2xl p-6 ring-1 ${\n              isCovalba\n                ? 'bg-[#142333] text-white ring-transparent'\n                : 'bg-white text-foreground ring-foreground/[0.06]'\n            }`}\n          >\n            <div className=\"mb-6 flex items-start justify-between gap-4\">\n              <h3 className=\"font-satoshi text-xl font-black leading-tight\">{option}</h3>\n              {isCovalba && (\n                <span className=\"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-teal-vivid/15 text-teal-vivid\">\n                  <Check className=\"h-4 w-4\" strokeWidth={2.4} />\n                </span>\n              )}\n            </div>\n            <p className={`font-satoshi text-3xl font-black leading-none ${isCovalba ? 'text-teal-vivid' : 'text-terracotta'}`}>\n              {dynamicInvestment}\n            </p>\n            {pricePerM2 && (\n              <p className={`mt-2 font-body text-xs font-semibold ${isCovalba ? 'text-white/45' : 'text-foreground/40'}`}>\n                basis {new Intl.NumberFormat('en-US').format(surface)} m² · {pricePerM2.toLocaleString('en-US')} €/m²\n              </p>\n            )}\n            <dl className=\"mt-6 space-y-3 border-t border-current/10 pt-5\">\n              {[duration, activity].filter(Boolean).map((row) => (\n                <div key={row![0]} className=\"grid grid-cols-[0.75fr_1fr] gap-4\">\n                  <dt className={`font-body text-xs font-bold uppercase tracking-[0.14em] ${isCovalba ? 'text-white/38' : 'text-foreground/35'}`}>\n                    {displayCell(row![0])}\n                  </dt>\n                  <dd className={`font-body text-sm leading-relaxed ${isCovalba ? 'text-white/74' : 'text-foreground/62'}`}>\n                    {displayCell(row![optionIndex + 1])}\n                  </dd>\n                </div>\n              ))}\n            </dl>\n          </article>\n        );\n      })}\n    </div>\n  );\n};\n\nconst ROICalculator = ({\n  page,\n  surface,\n  selectedSituation,\n  treatmentRange,\n  onSurfaceChange,\n  onSituationChange,\n}: {\n  page: RoofPageData;\n  surface: number;\n  selectedSituation: string;\n  treatmentRange: ReturnType<typeof getTreatmentRange>;\n  onSurfaceChange: (surface: number) => void;\n  onSituationChange: (situation: string) => void;\n}) => {\n  const treatmentLow = surface * treatmentRange.min;\n  const treatmentHigh = surface * treatmentRange.max;\n  const referencePrice = selectedSituation.toLowerCase().includes('insulation') ? 150 : 90;\n  const referenceCost = surface * referencePrice;\n  const potentialGap = Math.max(referenceCost - treatmentHigh, 0);\n\n  return (\n    <div className=\"mb-10 rounded-[2rem] bg-[#142333] p-5 text-white shadow-[0_34px_90px_-64px_hsl(var(--navy)/0.65)] lg:p-8\">\n      <div className=\"grid gap-7 lg:grid-cols-[0.88fr_1.12fr] lg:items-end\">\n        <div>\n          <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-teal-vivid/70\">\n            Indicative calculator\n          </p>\n          <h3 className=\"mt-4 max-w-lg font-satoshi text-2xl font-black leading-tight lg:text-3xl\">\n            Adjust the surface and the condition of the roof.\n          </h3>\n          <p className=\"mt-3 max-w-lg font-body text-sm leading-relaxed text-white/52\">\n            Default: 5,000 m², a typical surface for a warehouse or industrial building to treat.\n          </p>\n        </div>\n\n        <div className=\"grid gap-3 sm:grid-cols-[0.72fr_1.28fr]\">\n          <label className=\"block\">\n            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n              Surface\n            </span>\n            <input\n              type=\"number\"\n              min={100}\n              step={100}\n              value={surface}\n              onChange={(event) => onSurfaceChange(Math.max(100, Number(event.target.value) || 100))}\n              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n              aria-label=\"Surface to treat in square meters\"\n            />\n          </label>\n          <label className=\"block\">\n            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n              Situation\n            </span>\n            <select\n              value={selectedSituation}\n              onChange={(event) => onSituationChange(event.target.value)}\n              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n              aria-label=\"Roof situation\"\n            >\n              {page.roi.situation.rows.map((row) => (\n                <option key={row[0]} value={row[0]} className=\"bg-card text-foreground\">\n                  {row[0]}\n                </option>\n              ))}\n            </select>\n          </label>\n        </div>\n      </div>\n\n      <div className=\"mt-7 grid gap-3 md:grid-cols-3\">\n        {[\n          {\n            label: treatmentRange.label,\n            value: `${formatCurrency(treatmentLow)} - ${formatCurrency(treatmentHigh)}`,\n            detail: `${treatmentRange.min}-${treatmentRange.max} €/m² depending on preparation`,\n            valueClass: 'text-teal-vivid',\n          },\n          {\n            label: 'Reference refurbishment',\n            value: `~${formatCurrency(referenceCost)}`,\n            detail: `${referencePrice} €/m², order of magnitude for comparison`,\n            valueClass: 'text-terracotta',\n          },\n          {\n            label: 'Potential gap',\n            value: `up to ${formatCurrency(potentialGap)}`,\n            detail: 'Heavy investment avoided or deferred if the substrate is viable',\n            valueClass: 'text-teal-vivid',\n          },\n        ].map((item) => (\n          <article key={item.label} className=\"rounded-2xl bg-white/[0.07] p-5 ring-1 ring-white/10\">\n            <p className=\"font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/38\">{item.label}</p>\n            <p className={`mt-3 font-satoshi text-2xl font-black leading-tight ${item.valueClass}`}>{item.value}</p>\n            <p className=\"mt-2 font-body text-xs leading-relaxed text-white/45\">{item.detail}</p>\n          </article>\n        ))}\n      </div>\n      <p className=\"mt-4 font-body text-xs leading-relaxed text-white/38\">\n        Indicative estimate: the study adjusts the price based on access, preparation, detailing points and the actual condition of the substrate.\n      </p>\n    </div>\n  );\n};\n\nconst getSituationImage = (recommendation: string) => {\n  const lower = recommendation.toLowerCase();\n  if (lower.includes('covametal')) return covaMetalProduct;\n  if (lower.includes('covaseal')) return covaSealProduct;\n  if (lower.includes('cova')) return covaTherm20Product;\n  return covaTherm8Product;\n};\n\nconst SituationCards = ({ rows }: { rows: string[][] }) => (\n  <div className=\"grid gap-4 md:grid-cols-2\">\n    {rows.map((row) => (\n      <article key={row[0]} className=\"grid gap-4 rounded-2xl bg-white p-4 ring-1 ring-foreground/[0.06] sm:grid-cols-[128px_1fr] lg:grid-cols-[190px_1fr] lg:gap-5 lg:p-5\">\n        <div className=\"flex aspect-[4/3] items-center justify-center rounded-xl bg-gradient-to-br from-cream to-white p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.75),_0_18px_44px_-34px_hsl(var(--navy)/0.45)] ring-1 ring-foreground/[0.04] sm:aspect-square lg:p-6\">\n          <img src={getSituationImage(row[1])} alt=\"\" className=\"max-h-full max-w-full object-contain\" loading=\"lazy\" decoding=\"async\" />\n        </div>\n        <div className=\"self-center\">\n          <p className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{row[0]}</p>\n          <p className=\"mt-2 font-body text-sm font-bold text-primary\">{row[1]}</p>\n          <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/58\">{row[2]}</p>\n        </div>\n      </article>\n    ))}\n  </div>\n);\n\nconst ROISection = ({ page }: { page: RoofPageData }) => {\n  const [surface, setSurface] = useState(5000);\n  const [selectedSituation, setSelectedSituation] = useState(page.roi.situation.rows[0]?.[0] ?? '');\n  const treatmentRange = getTreatmentRange(page, selectedSituation);\n\n  return (\n    <section className=\"bg-cream py-20 lg:py-32\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto mb-12 max-w-3xl text-center lg:mb-16\">\n          <div className=\"flex justify-center\">\n            <Label>{page.roi.badge}</Label>\n          </div>\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-[3rem]\">\n            {page.roi.title}\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-foreground/58 lg:text-lg\">\n            {page.roi.intro}\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mb-12 grid gap-4 md:grid-cols-3\">\n          {[\n            ['20-25 €/m²', 'to treat and cool down'],\n            ['8-10 years', 'of refurbishment deferred'],\n            ['0 downtime', 'during the works'],\n          ].map(([value, label]) => (\n            <article key={value} className=\"rounded-2xl bg-white p-6 text-center ring-1 ring-foreground/[0.06]\">\n              <p className=\"font-satoshi text-4xl font-black leading-none text-primary\">{value}</p>\n              <p className=\"mt-3 font-body text-sm font-semibold text-foreground/52\">{label}</p>\n            </article>\n          ))}\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mb-8 max-w-2xl\">\n          <div>\n            <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n              Compare the options\n            </p>\n            <h3 className=\"mt-4 font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n              A quick read on the budget.\n            </h3>\n          </div>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <ROICalculator\n          page={page}\n          surface={surface}\n          selectedSituation={selectedSituation}\n          treatmentRange={treatmentRange}\n          onSurfaceChange={setSurface}\n          onSituationChange={setSelectedSituation}\n        />\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <RoiOptionCards page={page} surface={surface} treatmentRange={treatmentRange} />\n        {page.roi.comparison && page.roi.afterComparison && (\n          <p className=\"mt-5 max-w-3xl font-body text-sm leading-relaxed text-foreground/52 lg:text-base\">\n            {page.roi.afterComparison}\n          </p>\n        )}\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mt-16 rounded-[2rem] bg-white p-5 ring-1 ring-foreground/[0.06] lg:mt-20 lg:p-8\">\n          <div className=\"mb-6 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n            <div>\n              <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n                Decide on the actual condition\n              </p>\n              <h3 className=\"mt-4 max-w-xl font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n                Which solution for your roof?\n              </h3>\n            </div>\n            <PrimaryCTA label={page.roi.cta} href={CTA_HREF} />\n          </div>\n          <SituationCards rows={page.roi.situation.rows} />\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n  );\n};\n\nconst DiagnosticCTA = ({ title, text }: { title: string; text: string }) => (\n  <section className=\"relative overflow-hidden bg-[#182A3A] py-14 text-white lg:py-20\">\n    <div\n      aria-hidden=\"true\"\n      className=\"absolute inset-0 opacity-35\"\n      style={{\n        backgroundImage:\n          'radial-gradient(circle at 16% 20%, rgba(83, 207, 166, 0.22), transparent 28%), linear-gradient(115deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 120px)',\n      }}\n    />\n    <div className=\"container relative mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto grid max-w-5xl gap-7 lg:grid-cols-[1fr_auto] lg:items-center\">\n          <div>\n            <h2 className=\"font-satoshi text-2xl font-black leading-tight lg:text-4xl\">{title}</h2>\n            <p className=\"mt-3 max-w-2xl font-body text-sm leading-relaxed text-white/68 lg:text-base\">{text}</p>\n          </div>\n          <div className=\"flex flex-col gap-3 sm:flex-row lg:justify-end\">\n            <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n          </div>\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst MethodSection = ({ page }: { page: RoofPageData }) => {\n  const [videoOpen, setVideoOpen] = useState(false);\n\n  return (\n    <section className=\"bg-white py-16 lg:py-32\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n        <div className=\"flex flex-col items-start gap-12 lg:flex-row lg:gap-20\">\n          <div className=\"lg:sticky lg:top-[120px] lg:w-[40%]\">\n            <ScrollReveal>\n              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/50\">\n                The process\n              </p>\n              <h2\n                className=\"mb-6 font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                4 steps. Zero interruption.\n                <span className=\"text-foreground/30\"> Zero risk.</span>\n              </h2>\n              <p className=\"mb-8 font-body text-base leading-relaxed text-muted-foreground lg:text-lg\">\n                {page.method.reassurance}\n              </p>\n              <button\n                onClick={() => setVideoOpen(true)}\n                className=\"group flex items-center gap-3 text-primary transition-colors hover:text-primary\"\n              >\n                <span className=\"flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 transition-colors group-hover:bg-primary/20\">\n                  <Play className=\"h-5 w-5\" />\n                </span>\n                <span className=\"font-body text-sm font-semibold\">Watch a project in 2 minutes</span>\n              </button>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"relative lg:w-[60%]\">\n            <div className=\"absolute bottom-4 left-5 top-4 w-px bg-gradient-to-b from-primary/25 via-primary/10 to-transparent lg:left-6\" />\n            <div className=\"flex flex-col\">\n              {page.method.steps.map((step, index) => {\n                const Icon = methodIcons[index] ?? CheckCircle2;\n                return (\n                  <ScrollReveal key={step.title}>\n                    <div className=\"group relative flex items-start gap-5 py-8 lg:gap-7 lg:py-10\">\n                      <span className=\"relative z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-primary/20 bg-primary/10 transition-colors duration-500 group-hover:bg-primary/15 lg:h-12 lg:w-12\">\n                        <Icon className=\"h-4 w-4 text-primary lg:h-5 lg:w-5\" />\n                      </span>\n                      <div className=\"pt-1\">\n                        <span className=\"mb-2 block font-body text-xs font-bold uppercase tracking-widest text-primary/50\">\n                          Step 0{index + 1}\n                        </span>\n                        <h3 className=\"mb-2 font-satoshi text-xl font-bold text-foreground lg:text-2xl\">\n                          {step.title}\n                        </h3>\n                        <p className=\"max-w-md font-body text-sm leading-relaxed text-muted-foreground lg:text-base\">\n                          {step.text}\n                        </p>\n                      </div>\n                    </div>\n                    {index < page.method.steps.length - 1 && <div className=\"ml-5 h-px bg-border lg:ml-6\" />}\n                  </ScrollReveal>\n                );\n              })}\n            </div>\n\n            <ScrollReveal>\n              <div className=\"ml-16 mt-4 lg:ml-20\">\n                <p className=\"font-body text-xs font-medium text-muted-foreground/50\">\n                  Average duration: 3 days · No interruption of activity\n                </p>\n              </div>\n            </ScrollReveal>\n          </div>\n        </div>\n      </div>\n\n      <Dialog open={videoOpen} onOpenChange={setVideoOpen}>\n        <DialogContent className=\"max-w-4xl overflow-hidden border-none bg-black p-0\">\n          <div className=\"aspect-video\">\n            <iframe\n              src=\"https://www.youtube.com/embed/9HOB5OVH0Ac?autoplay=1\"\n              className=\"h-full w-full\"\n              allow=\"autoplay; encrypted-media\"\n              allowFullScreen\n              title=\"Covalba Cool Roof, a project in 2 minutes\"\n            />\n          </div>\n        </DialogContent>\n      </Dialog>\n    </section>\n  );\n};\n\nconst ProofSection = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-[#192A3A] py-20 text-white lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mb-12 grid gap-8 lg:grid-cols-[0.9fr_1.1fr] lg:items-end\">\n          <div>\n            <Label light>{page.proof.badge}</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight sm:text-4xl lg:text-5xl\">{page.proof.title}</h2>\n          </div>\n          <p className=\"max-w-xl font-body text-base leading-relaxed text-white/62\">\n            Field figures to be confirmed project by project, used here as performance benchmarks.\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal stagger>\n        <div className=\"grid gap-4 md:grid-cols-3\">\n          {page.proof.figures.map((figure, index) => {\n            const Icon = proofIcons[index] ?? BadgeCheck;\n            return (\n              <article key={figure.value} className=\"rounded-2xl border border-white/10 bg-white/[0.045] p-7\">\n                <Icon className=\"mb-6 h-6 w-6 text-teal-vivid\" strokeWidth={1.7} />\n                <p className=\"font-satoshi text-3xl font-black leading-none text-white lg:text-4xl\">{figure.value}</p>\n                <p className=\"mt-3 font-body text-sm leading-relaxed text-white/60\">{figure.label}</p>\n              </article>\n            );\n          })}\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mt-14 border-t border-white/10 pt-10\">\n          <p className=\"mb-8 text-center font-body text-[10px] font-bold uppercase tracking-[0.22em] text-white/42\">\n            Client references\n          </p>\n          <div className=\"flex flex-wrap items-center justify-center gap-x-10 gap-y-7 lg:gap-x-14\">\n            {logos.map((logo) => (\n              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n                <img\n                  src={logo.url}\n                  alt={logo.name}\n                  className=\"max-h-7 max-w-full object-contain opacity-45 transition-opacity duration-300 hover:opacity-80\"\n                  style={{ filter: 'brightness(0) invert(1)' }}\n                  loading=\"lazy\"\n                />\n              </span>\n            ))}\n          </div>\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst FAQSection = ({ faq }: { faq: RoofPageData['faq'] }) => (\n  <section className=\"bg-cream py-20 lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <div className=\"grid gap-10 lg:grid-cols-[0.85fr_1.15fr] lg:gap-16\">\n        <ScrollReveal>\n          <div className=\"lg:sticky lg:top-24\">\n            <Label>FAQ</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n              Your questions,\n              <br />\n              <span className=\"text-foreground/40\">our answers.</span>\n            </h2>\n            <a href=\"#contact\" className=\"group mt-8 inline-flex items-center gap-2 font-body text-sm font-semibold text-primary transition-all hover:gap-3\">\n              Another question? Let's talk\n              <ArrowRight className=\"h-4 w-4\" />\n            </a>\n          </div>\n        </ScrollReveal>\n\n        <ScrollReveal>\n          <Accordion type=\"single\" collapsible className=\"space-y-3\">\n            {faq.map((item, index) => (\n              <AccordionItem\n                key={item.q}\n                value={`roof-faq-${index}`}\n                className=\"rounded-xl border border-foreground/[0.06] bg-white px-5 shadow-[0_12px_34px_rgba(24,42,58,0.04)] data-[state=open]:border-primary/20 lg:px-6\"\n              >\n                <AccordionTrigger className=\"py-5 text-left font-satoshi text-base font-bold text-foreground hover:no-underline lg:text-[17px]\">\n                  {item.q}\n                </AccordionTrigger>\n                <AccordionContent className=\"pb-5 font-body text-[14.5px] leading-relaxed text-foreground/67 lg:text-[15px]\">\n                  {item.a}\n                </AccordionContent>\n              </AccordionItem>\n            ))}\n          </Accordion>\n        </ScrollReveal>\n      </div>\n    </div>\n  </section>\n);\n\nconst FinalCTA = ({ page }: { page: RoofPageData }) => (\n  <section id=\"contact\" className=\"overflow-hidden bg-[#182A3A] py-20 text-white lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto max-w-4xl text-center\">\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-white sm:text-4xl lg:text-5xl\">\n            {page.finalCta.title}\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-white/72 lg:text-lg\">\n            {page.finalCta.text}\n          </p>\n          <div className=\"mt-9 flex flex-col gap-3 sm:flex-row sm:justify-center\">\n            <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n          </div>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <ul className=\"mx-auto mt-12 grid max-w-3xl gap-3 sm:grid-cols-3\">\n          {page.finalCta.reassurances.map((item) => (\n            <li key={item} className=\"flex items-center justify-center gap-2.5 rounded-lg border border-white/[0.12] bg-white/[0.045] px-4 py-3\">\n              <CalendarCheck className=\"h-4 w-4 shrink-0 text-teal-vivid\" strokeWidth={1.8} />\n              <span className=\"font-body text-sm text-white/[0.76]\">{item}</span>\n            </li>\n          ))}\n        </ul>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst SEOContent = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-white py-20 lg:py-28\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <article className=\"mx-auto max-w-3xl\">\n          <p className=\"mb-5 font-body text-[10px] font-bold uppercase tracking-[0.25em] text-primary/55\">\n            Substrate guide\n          </p>\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground lg:text-4xl\">\n            Everything you need to know about {page.eyebrow.toLowerCase()}.\n          </h2>\n          <p className=\"mt-6 font-body text-base leading-relaxed text-foreground/68 lg:text-lg\">\n            {page.seo.intro}\n          </p>\n\n          <div className=\"mt-10 space-y-9\">\n            {page.seo.sections.map((section) => (\n              <section key={section.title}>\n                <h3 className=\"font-satoshi text-2xl font-bold leading-tight text-foreground\">{section.title}</h3>\n                <p className=\"mt-3 font-body text-base leading-relaxed text-foreground/66\">{section.text}</p>\n              </section>\n            ))}\n          </div>\n\n          <div className=\"mt-12 rounded-2xl bg-cream p-6 ring-1 ring-foreground/[0.05]\">\n            <h3 className=\"font-satoshi text-lg font-bold text-foreground\">Sources</h3>\n            <ul className=\"mt-4 space-y-2\">\n              {page.seo.sources.map((source) => (\n                <li key={source.href}>\n                  <a href={source.href} className=\"font-body text-sm leading-relaxed text-primary underline-offset-4 hover:underline\" target=\"_blank\" rel=\"noreferrer\">\n                    {source.label}\n                  </a>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </article>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst buildFaqSchema = (page: RoofPageData) => ({\n  '@context': 'https://schema.org',\n  '@type': 'FAQPage',\n  mainEntity: page.faq.map((item) => ({\n    '@type': 'Question',\n    name: item.q,\n    acceptedAnswer: {\n      '@type': 'Answer',\n      text: item.a,\n    },\n  })),\n});\n\nconst buildBreadcrumbSchema = (page: RoofPageData) => ({\n  '@context': 'https://schema.org',\n  '@type': 'BreadcrumbList',\n  itemListElement: [\n    { '@type': 'ListItem', position: 1, name: 'Home', item: `${SITE_URL}/en` },\n    { '@type': 'ListItem', position: 2, name: 'Roofs', item: `${SITE_URL}/en` },\n    { '@type': 'ListItem', position: 3, name: page.breadcrumb, item: page.canonical },\n  ],\n});\n\nconst RoofPageTemplateEN = ({ page }: RoofPageTemplateENProps) => {\n  usePageMeta({\n    title: page.title,\n    description: page.description,\n    canonical: page.canonical,\n  });\n  useJsonLd(`roof-en-${page.slug}-faq`, buildFaqSchema(page));\n  useJsonLd(`roof-en-${page.slug}-breadcrumb`, buildBreadcrumbSchema(page));\n\n  return (\n    <>\n      <NavbarEN />\n      <main>\n        <RoofHero page={page} />\n        <ProblemSection page={page} />\n        <SolutionSection page={page} />\n        <BenefitsSection page={page} />\n        <ROISection page={page} />\n        <DiagnosticCTA title={page.midCta.title} text={page.midCta.text} />\n        <MethodSection page={page} />\n        <ProofSection page={page} />\n        <FAQSection faq={page.faq} />\n        <FinalCTA page={page} />\n        <SEOContent page={page} />\n      </main>\n      <FooterEN />\n      <StickyMobileCTAEN />\n    </>\n  );\n};\n\nexport default RoofPageTemplateEN;\n"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":131,"cache_creation_input_tokens":770,"cache_read_input_tokens":97116,"output_tokens":25026,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":770},"inference_geo":"not_available","iterations":[{"input_tokens":131,"output_tokens":25026,"cache_read_input_tokens":97116,"cache_creation_input_tokens":770,"cache_creation":{"ephemeral_5m_input_tokens":770,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CcHKUx6GwCqRoUoUWMbMy","attributionAgent":"general-purpose","type":"assistant","uuid":"ac9aef3e-ac37-4eac-9f32-f22e4d914295","timestamp":"2026-06-22T00:07:15.440Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"ac9aef3e-ac37-4eac-9f32-f22e4d914295","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_012sXozubke9NR11oHBGjvSS","type":"tool_result","content":"File created successfully at: /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/en/roof/RoofPageTemplateEN.tsx (file state is current in your context — no need to Read it back)"}]},"uuid":"af871b56-eb69-4f6f-b4d1-e5f34048fcc0","timestamp":"2026-06-22T00:07:15.496Z","toolUseResult":{"type":"create","filePath":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/en/roof/RoofPageTemplateEN.tsx","content":"import { useState } from 'react';\nimport {\n  ArrowRight,\n  BadgeCheck,\n  CalendarClock,\n  CalendarCheck,\n  Check,\n  CheckCircle2,\n  ChevronDown,\n  Factory,\n  HelpCircle,\n  Play,\n  ShieldCheck,\n  ShieldAlert,\n  Sparkles,\n  SunMedium,\n  Thermometer,\n  TrendingDown,\n  Users,\n  Wrench,\n  ClipboardCheck,\n  type LucideIcon,\n} from 'lucide-react';\n\nimport FooterEN from '@/components/en/FooterEN';\nimport NavbarEN from '@/components/en/NavbarEN';\nimport ScrollReveal from '@/components/ScrollReveal';\nimport StickyMobileCTAEN from '@/components/en/StickyMobileCTAEN';\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';\nimport { Dialog, DialogContent } from '@/components/ui/dialog';\nimport { useJsonLd, usePageMeta } from '@/hooks/usePageMeta';\nimport { type RoofPageData, type TextCard } from '@/data/roofPages';\nimport coolRoofLayers from '@/assets/cool-roof-layers.webp';\nimport covaMetalProduct from '@/assets/produits/Covalba-CovaMetal20-Duo-Produit.webp';\nimport covaSealProduct from '@/assets/produits/Covalba-CovaSeal20-Duo-Produit.webp';\nimport covaTherm8Product from '@/assets/produits/Covalba-CovaTherm8-Produit.webp';\nimport covaTherm20Product from '@/assets/produits/Covalba-CovaTherm20-Produit.webp';\n\ninterface RoofPageTemplateENProps {\n  page: RoofPageData;\n}\n\nconst SITE_URL = 'https://www.covalba.fr';\n\n// Internal route for all CTAs on the EN site. There is no dedicated EN\n// diagnostic/contact route yet, so every conversion CTA points to the EN home.\nconst CTA_HREF = '/en';\n\nconst cardIcons: LucideIcon[] = [SunMedium, Wrench, Factory, ShieldCheck];\nconst methodIcons: LucideIcon[] = [ClipboardCheck, CalendarClock, ShieldCheck, TrendingDown];\nconst proofIcons: LucideIcon[] = [Thermometer, ShieldCheck, BadgeCheck];\nconst problemIcons: LucideIcon[] = [Users, SunMedium, ShieldAlert];\n\nconst logos = [\n  { name: 'Continental', url: '/logos/logo-continental.svg' },\n  { name: 'Thales', url: '/logos/logo-thales.svg' },\n  { name: 'Nestlé', url: '/logos/logo-nestle.svg' },\n  { name: 'SNCF', url: '/logos/logo-sncf.svg' },\n  { name: 'Leclerc', url: '/logos/logo-leclerc.svg' },\n  { name: 'Super U', url: '/logos/logo-super-u.svg' },\n];\n\nconst displayCell = (value: string) => value.replace(/EUR/g, '€').replace(/m2/g, 'm²');\n\nconst formatCurrency = (value: number) =>\n  `${new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(Math.round(value))} €`;\n\nconst parsePricePerM2 = (value: string) => {\n  const match = displayCell(value).match(/\\(([\\d,.]+)\\s*€\\/m²\\)/);\n  if (!match) return null;\n\n  return Number.parseFloat(match[1].replace(',', '.'));\n};\n\nconst getTreatmentRange = (page: RoofPageData, situation: string) => {\n  const text = `${page.slug} ${situation}`.toLowerCase();\n\n  if (\n    text.includes('covaseal') ||\n    text.includes('membrane-bitumineuse') ||\n    text.includes('toiture-plate') ||\n    text.includes('aging') ||\n    text.includes('leak')\n  ) {\n    return { min: 28, max: 38, label: 'CovaSeal 20 estimate' };\n  }\n\n  if (\n    text.includes('covametal') ||\n    text.includes('bac-acier') ||\n    text.includes('corrosion') ||\n    text.includes('steel')\n  ) {\n    return { min: 24, max: 32, label: 'CovaMetal 20 estimate' };\n  }\n\n  if (text.includes('fibrociment') || text.includes('fiber') || text.includes('sheets')) {\n    return { min: 22, max: 28, label: 'CovaTherm sensitive substrate' };\n  }\n\n  return { min: 20, max: 25, label: 'CovaTherm estimate' };\n};\n\nconst getFallbackPricePerM2 = (option: string, optionIndex: number, treatmentMidpoint: number) => {\n  const lower = option.toLowerCase();\n\n  if (optionIndex === 0) return treatmentMidpoint;\n  if (lower.includes('anticorrosion') || lower.includes('repaint')) return 16;\n  if (lower.includes('replacement') || lower.includes('replace')) return 120;\n  if (lower.includes('new membrane') || lower.includes('membrane')) return 90;\n  if (lower.includes('insulation')) return 150;\n\n  return null;\n};\n\nconst benefitImages = [\n  '/images/toitures/benefit-confort.webp',\n  '/images/toitures/benefit-protection-toiture.webp',\n  '/images/toitures/benefit-clim.webp',\n  '/images/toitures/benefit-chantier-evite.webp',\n];\n\nconst feedbackRoofBenefitImages = [\n  '/images/toitures/feedback-batiment-tempere.png',\n  '/images/toitures/feedback-etancheite.png',\n  '/images/toitures/feedback-economie-clim-1.png',\n  '/images/toitures/feedback-economie-clim-3.png',\n];\n\nconst getBenefitImage = (page: RoofPageData, benefit: TextCard, index: number) => {\n  if (['membrane-bitumineuse', 'toiture-plate'].includes(page.slug)) {\n    return feedbackRoofBenefitImages[index] ?? feedbackRoofBenefitImages[0];\n  }\n\n  const text = `${benefit.title} ${benefit.text}`.toLowerCase();\n\n  if (page.slug === 'bac-acier' && (text.includes('steel') || text.includes('corrosion') || text.includes('rust'))) {\n    return '/images/toitures/support-bac-acier-cool.webp';\n  }\n\n  if (text.includes('bill') || text.includes('cooling') || text.includes('air conditioning') || text.includes('cold')) {\n    return '/images/toitures/benefit-clim.webp';\n  }\n\n  if (\n    text.includes('project') ||\n    text.includes('refurbishment') ||\n    text.includes('replacement') ||\n    text.includes('removal') ||\n    text.includes('strip')\n  ) {\n    return '/images/toitures/benefit-chantier-evite.webp';\n  }\n\n  if (\n    text.includes('protect') ||\n    text.includes('waterproof') ||\n    text.includes('corrosion') ||\n    text.includes('lifespan') ||\n    text.includes('durab')\n  ) {\n    return '/images/toitures/benefit-protection-toiture.webp';\n  }\n\n  return benefitImages[index] ?? benefitImages[0];\n};\n\nconst supportVisuals: Record<string, { hot: string; cool: string; hotLabel: string; coolLabel: string }> = {\n  'membrane-bitumineuse': {\n    hot: '/images/toitures/support-bitume-chaud.webp',\n    cool: '/images/toitures/feedback-economie-clim-2.png',\n    hotLabel: 'Without cool roof: bitumen membrane absorbing heat',\n    coolLabel: 'With cool roof: reflective membrane',\n  },\n  'bac-acier': {\n    hot: '/images/toitures/support-bac-acier-chaud.webp',\n    cool: '/images/toitures/support-bac-acier-cool.webp',\n    hotLabel: 'Without cool roof: overheating metal',\n    coolLabel: 'With cool roof: reflective, protected steel deck',\n  },\n  'toiture-plate': {\n    hot: '/images/toitures/support-toiture-plate-chaud.webp',\n    cool: '/images/toitures/feedback-economie-clim-2.png',\n    hotLabel: 'Without cool roof: roof terrace absorbing heat',\n    coolLabel: 'With cool roof: reflective roof terrace',\n  },\n  fibrociment: {\n    hot: '/images/toitures/support-fibrociment-chaud.webp',\n    cool: '/images/toitures/support-fibrociment-cool.webp',\n    hotLabel: 'Without cool roof: fiber cement absorbing heat',\n    coolLabel: 'With cool roof: protected sheets',\n  },\n  'tuiles-ciment': {\n    hot: '/images/toitures/support-tuiles-ciment-chaud.webp',\n    cool: '/images/toitures/support-tuiles-ciment-cool.webp',\n    hotLabel: 'Without cool roof: tiles storing heat',\n    coolLabel: 'With cool roof: reflective tiles',\n  },\n};\n\nconst layerTitles = ['Bonding primer', 'Reflective resin', 'Protective top coat'];\n\nconst layerColors = [\n  {\n    pill: 'bg-terracotta text-white',\n    dot: 'bg-terracotta',\n    chevron: 'text-terracotta',\n  },\n  {\n    pill: 'bg-foreground text-white',\n    dot: 'bg-foreground',\n    chevron: 'text-foreground',\n  },\n  {\n    pill: 'bg-primary text-white',\n    dot: 'bg-primary',\n    chevron: 'text-primary',\n  },\n];\n\nconst layerPositions = [\n  { top: '56.7%', left: '36.5%' },\n  { top: '48.2%', left: '36.5%' },\n  { top: '36.3%', left: '35.6%' },\n];\n\nconst getProblemTitle = (title: string) => {\n  const map: Record<string, { strong: string; muted: string }> = {\n    'A black bitumen roof is a radiator sitting on top of your building.': {\n      strong: 'Your roof heats up.',\n      muted: 'Your building takes the hit.',\n    },\n    'Steel decking heats up fast, rusts fast, and wears out fast.': {\n      strong: 'The metal heats up fast.',\n      muted: 'The roof wears out too.',\n    },\n    'Flat, your roof takes the sun all day long.': {\n      strong: 'Your roof terrace takes it all.',\n      muted: 'Heat, UV, waterproofing.',\n    },\n    'Fiber cement heats up, ages, and is expensive to remove.': {\n      strong: 'Fiber cement heats up.',\n      muted: 'Removing it is expensive.',\n    },\n    'Dark cement tiles, overheating attic space.': {\n      strong: 'The tiles store the heat.',\n      muted: 'The attic space takes it.',\n    },\n  };\n\n  return map[title] ?? { strong: title, muted: '' };\n};\n\nconst getLayerDescription = (index: number) => {\n  if (index === 0) {\n    return 'Adapts to the existing substrate. Every roof has its own chemistry, and every chemistry calls for tailored preparation and bonding.';\n  }\n\n  if (index === 1) {\n    return 'This is the heart of the system. The surface reflects solar radiation instead of absorbing it, so less heat enters the building.';\n  }\n\n  return 'A top coat protects the resin from UV, pollution and premature aging to preserve performance over time.';\n};\n\nconst getLayerExpert = (index: number) => {\n  const notes = [\n    'An unsuitable primer means a system that blisters and peels off. The substrate decides everything.',\n    'Cool roof treats the cause: the roof heats up less, so the building takes on less heat.',\n    'The difference between a paint job and an investment is how it holds up over time. The finish is there to protect performance.',\n  ];\n\n  return notes[index] ?? notes[0];\n};\n\nconst Label = ({ children, light = false }: { children: string; light?: boolean }) => (\n  <div className=\"mb-5 flex items-center gap-3\">\n    <span className={`h-px w-9 ${light ? 'bg-teal-vivid/70' : 'bg-primary/65'}`} aria-hidden=\"true\" />\n    <span\n      className={`font-body text-[11px] font-bold uppercase tracking-[0.22em] ${\n        light ? 'text-teal-vivid' : 'text-primary'\n      }`}\n    >\n      {children}\n    </span>\n  </div>\n);\n\nconst PrimaryCTA = ({ label, href }: { label: string; href: string }) => (\n  <a\n    href={href}\n    className=\"group inline-flex items-center justify-center gap-2 rounded-full cta-gradient py-2.5 pl-6 pr-2 font-semibold text-white\"\n  >\n    <span className=\"text-sm\">{label}</span>\n    <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n      <ArrowRight className=\"h-3.5 w-3.5\" />\n    </span>\n  </a>\n);\n\nconst RoofHero = ({ page }: { page: RoofPageData }) => {\n  const [form, setForm] = useState({ climate: '', surface: '', email: '' });\n  const [submitted, setSubmitted] = useState(false);\n  const surfaceNum = parseFloat(form.surface) || 0;\n  const estimate = surfaceNum ? `${new Intl.NumberFormat('en-US').format(surfaceNum * 22.5)} €` : '20-25 €/m²';\n\n  return (\n    <section\n      aria-labelledby={`${page.slug}-hero-title`}\n      className=\"relative isolate flex min-h-[100svh] flex-col overflow-hidden bg-navy text-cream\"\n    >\n      <img\n        src={page.heroImage}\n        alt={page.heroImageAlt}\n        className=\"absolute inset-0 -z-10 size-full scale-[1.02] object-cover\"\n        style={page.slug === 'membrane-bitumineuse' ? { objectPosition: 'center 28%' } : undefined}\n        loading=\"eager\"\n        decoding=\"async\"\n      />\n      <div\n        aria-hidden=\"true\"\n        className=\"absolute inset-0 -z-10 hidden bg-gradient-to-r from-navy/[0.94] via-navy/62 to-navy/25 lg:block\"\n      />\n      <div\n        aria-hidden=\"true\"\n        className={`absolute inset-0 -z-10 bg-gradient-to-b lg:hidden ${\n          page.slug === 'membrane-bitumineuse'\n            ? 'from-navy/92 via-navy/58 to-navy/96'\n            : 'from-navy/84 via-navy/38 to-navy/96'\n        }`}\n      />\n      <div aria-hidden=\"true\" className=\"absolute inset-x-0 bottom-0 -z-10 h-[28%] bg-gradient-to-t from-navy via-navy/85 to-transparent\" />\n\n      <div className=\"container relative z-10 mx-auto flex flex-1 items-center px-6 pb-10 pt-24 lg:px-12 lg:pb-16 lg:pt-28\">\n        <div className=\"grid w-full grid-cols-1 items-center gap-10 lg:grid-cols-[55fr_45fr] lg:gap-12\">\n          <div className=\"max-w-2xl space-y-6 lg:space-y-8\">\n            <ScrollReveal>\n              <nav className=\"flex items-center gap-2 font-body text-xs text-cream/50\">\n                <a href=\"/en\" className=\"transition-colors hover:text-cream/75\">Home</a>\n                <span className=\"text-cream/28\">/</span>\n                <span>Roofs</span>\n                <span className=\"text-cream/28\">/</span>\n                <span className=\"text-cream/78\">{page.breadcrumb}</span>\n              </nav>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <h1\n                id={`${page.slug}-hero-title`}\n                className=\"font-satoshi font-bold leading-[1.02] text-white\"\n                style={{ fontSize: 'clamp(2.25rem, 4.6vw, 4rem)', letterSpacing: '-0.03em' }}\n              >\n                {page.h1}\n              </h1>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <p\n                className=\"max-w-xl font-satoshi font-medium leading-[1.3] text-cream/66\"\n                style={{ fontSize: 'clamp(1rem, 1.5vw, 1.375rem)', letterSpacing: '-0.005em' }}\n              >\n                {page.lead}\n              </p>\n            </ScrollReveal>\n\n            <ScrollReveal>\n              <ul className=\"hidden gap-4 pt-1 sm:flex sm:flex-wrap lg:gap-6\" aria-label=\"Key benefits\">\n                {page.heroStats.map((stat, index) => {\n                  const Icon = [Thermometer, ShieldCheck, TrendingDown][index] ?? BadgeCheck;\n                  return (\n                    <li\n                      key={stat.label}\n                      className={`flex items-start gap-3 ${index !== 0 ? 'sm:border-l sm:border-white/10 sm:pl-6' : ''}`}\n                    >\n                      <Icon className=\"mt-0.5 h-[18px] w-[18px] shrink-0 text-teal-vivid\" strokeWidth={1.5} />\n                      <div className=\"leading-tight\">\n                        <p className=\"font-satoshi text-[15px] font-bold text-white lg:text-base\">{stat.value}</p>\n                        <p className=\"mt-1 font-body text-[12.5px] text-cream/55 lg:text-[13px]\">{stat.label}</p>\n                      </div>\n                    </li>\n                  );\n                })}\n              </ul>\n            </ScrollReveal>\n          </div>\n\n          <ScrollReveal>\n            <div className=\"rounded-[1.5rem] border border-white/[0.08] bg-white/[0.04] p-1.5 backdrop-blur-sm\">\n              <div className=\"rounded-[calc(1.5rem-6px)] border border-white/[0.06] bg-[#0b1a2b]/80 p-4 shadow-[inset_0_1px_1px_rgba(255,255,255,0.06)] backdrop-blur-xl lg:p-7\">\n                {!submitted ? (\n                  <>\n                    <h2 className=\"mb-1 text-center font-satoshi text-xl font-bold text-white lg:text-2xl\">\n                      Estimate your potential savings\n                    </h2>\n                    <p className=\"mb-4 text-center font-body text-[12px] text-white/50\">\n                      Tailored reply within 24h\n                    </p>\n                    <form\n                      onSubmit={(event) => {\n                        event.preventDefault();\n                        setSubmitted(true);\n                      }}\n                      className=\"space-y-2.5 lg:space-y-3\"\n                    >\n                      <input\n                        value={page.breadcrumb}\n                        readOnly\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white outline-none\"\n                      />\n                      <select\n                        required\n                        value={form.climate}\n                        onChange={(e) => setForm({ ...form, climate: e.target.value })}\n                        className=\"w-full appearance-none rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      >\n                        <option value=\"\" disabled className=\"bg-card text-foreground\">Is the building air-conditioned?</option>\n                        {['Yes', 'No', 'Partially', 'To confirm'].map((option) => (\n                          <option key={option} value={option} className=\"bg-card text-foreground\">{option}</option>\n                        ))}\n                      </select>\n                      <input\n                        type=\"number\"\n                        placeholder=\"Roof surface (m²)\"\n                        required\n                        min=\"1\"\n                        value={form.surface}\n                        onChange={(e) => setForm({ ...form, surface: e.target.value })}\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      />\n                      <input\n                        type=\"email\"\n                        placeholder=\"Work email\"\n                        required\n                        value={form.email}\n                        onChange={(e) => setForm({ ...form, email: e.target.value })}\n                        className=\"w-full rounded-xl border border-white/[0.12] bg-white/[0.07] px-4 py-3 font-body text-sm text-white placeholder:text-white/40 transition-all duration-300 focus:border-white/25 focus:outline-none focus:ring-2 focus:ring-primary/50\"\n                      />\n\n                      <button\n                        type=\"submit\"\n                        className=\"group mt-1 flex w-full items-center justify-center gap-3 rounded-full cta-gradient py-3 font-semibold text-white active:scale-[0.98]\"\n                      >\n                        <span className=\"text-[15px]\">See my potential</span>\n                        <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-white/20 transition-transform duration-500 group-hover:translate-x-0.5\">\n                          <ArrowRight className=\"h-4 w-4\" />\n                        </span>\n                      </button>\n                    </form>\n\n                    <p className=\"mt-3 text-center font-body text-[11px] text-white/35\">\n                      No commitment · Study tailored to your substrate\n                    </p>\n                  </>\n                ) : (\n                  <div className=\"py-4 text-center\">\n                    <div className=\"mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-primary/20\">\n                      <ShieldCheck className=\"h-6 w-6 text-primary\" />\n                    </div>\n                    <p className=\"mb-1 font-body text-sm text-white/60\">Indicative basis for {new Intl.NumberFormat('en-US').format(surfaceNum)} m²</p>\n                    <p className=\"mb-4 font-satoshi text-2xl font-bold text-white\">{estimate}</p>\n                    <p className=\"mb-4 font-body text-[13px] text-white/50\">\n                      An advisor refines the estimate based on the actual condition of the roof.\n                    </p>\n                    <button onClick={() => setSubmitted(false)} className=\"font-body text-sm font-semibold text-primary hover:underline\">\n                      Edit my request\n                    </button>\n                  </div>\n                )}\n              </div>\n            </div>\n          </ScrollReveal>\n        </div>\n      </div>\n\n      <div className=\"relative z-10 hidden border-t border-white/10 bg-navy/78 backdrop-blur-md lg:block\">\n        <div className=\"container mx-auto flex items-center gap-8 px-12 py-5\">\n          <p className=\"shrink-0 font-body text-[10px] font-semibold uppercase tracking-[0.24em] text-cream/45\">\n            They trust us\n          </p>\n          <div className=\"flex flex-1 items-center justify-between gap-8\">\n            {logos.map((logo) => (\n              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n                <img\n                  src={logo.url}\n                  alt={logo.name}\n                  className=\"max-h-7 max-w-full object-contain opacity-65 brightness-0 invert transition-opacity duration-300 hover:opacity-95\"\n                  loading=\"eager\"\n                  decoding=\"async\"\n                />\n              </span>\n            ))}\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst ProblemSection = ({ page }: { page: RoofPageData }) => {\n  const title = getProblemTitle(page.problem.title);\n  const visual = supportVisuals[page.slug] ?? supportVisuals['membrane-bitumineuse'];\n\n  return (\n    <section className=\"relative overflow-hidden bg-cream py-16 lg:py-36\">\n      <div className=\"container relative mx-auto px-4 lg:px-8\">\n        <ScrollReveal>\n          <div className=\"mx-auto mb-14 max-w-3xl text-center lg:mb-20\">\n            <h2\n              className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n              style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n            >\n              {title.strong}\n              {title.muted && <span className=\"text-foreground/30\"> {title.muted}</span>}\n            </h2>\n          </div>\n        </ScrollReveal>\n\n        <div className=\"grid grid-cols-1 items-center gap-12 lg:grid-cols-12 lg:gap-16\">\n          <div className=\"order-2 lg:order-1 lg:col-span-5\">\n            <ScrollReveal>\n              <div className=\"relative\">\n                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_hsl(var(--terracotta)/0.18)_0%,_transparent_65%)] blur-2xl\" />\n                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_hsl(var(--terracotta-dark)/0.25),_0_10px_30px_-5px_hsl(var(--terracotta)/0.12)]\">\n                  <img\n                    src={visual.hot}\n                    alt=\"\"\n                    className=\"h-full w-full object-contain p-4\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                </div>\n              </div>\n              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-terracotta/60\">\n                {visual.hotLabel}\n              </p>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"order-1 lg:order-2 lg:col-span-7 lg:pl-12\">\n            <ScrollReveal stagger>\n              <div\n                className=\"space-y-8 border-l-2 pl-8 lg:space-y-10 lg:pl-10\"\n                style={{ borderImage: 'linear-gradient(to bottom, hsl(var(--terracotta)), hsl(var(--terracotta-dark))) 1' }}\n              >\n                {page.problem.cards.map((card, index) => {\n                  const Icon = problemIcons[index] ?? HelpCircle;\n                  return (\n                    <div key={card.title}>\n                      <div className=\"mb-2 flex items-center gap-3\">\n                        <Icon className=\"h-5 w-5 text-terracotta\" strokeWidth={1.8} />\n                        <h3\n                          className=\"font-satoshi text-xl font-bold text-foreground lg:text-2xl\"\n                          style={{ letterSpacing: '-0.02em', lineHeight: 1.2 }}\n                        >\n                          {card.title}\n                        </h3>\n                      </div>\n                      <p className=\"font-body text-base leading-relaxed text-foreground/55\">{card.text}</p>\n                    </div>\n                  );\n                })}\n              </div>\n            </ScrollReveal>\n          </div>\n        </div>\n\n        <div className=\"mt-20 grid grid-cols-1 items-center gap-12 lg:mt-28 lg:grid-cols-12 lg:gap-16\">\n          <div className=\"order-2 lg:order-2 lg:col-span-5\">\n            <ScrollReveal>\n              <div className=\"relative\">\n                <div className=\"pointer-events-none absolute -inset-8 rounded-[3rem] bg-[radial-gradient(ellipse_at_50%_50%,_rgba(32,150,140,0.18)_0%,_transparent_65%)] blur-2xl\" />\n                <div className=\"relative aspect-square overflow-hidden rounded-[2rem] bg-white shadow-[0_30px_80px_-15px_rgba(20,120,110,0.20),_0_10px_30px_-5px_rgba(32,150,140,0.10)]\">\n                  <img\n                    src={visual.cool}\n                    alt=\"\"\n                    className=\"h-full w-full object-contain p-4\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                </div>\n              </div>\n              <p className=\"mt-4 text-center font-body text-[11px] uppercase tracking-wide text-primary/60\">\n                {visual.coolLabel}\n              </p>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"order-1 lg:order-1 lg:col-span-7 lg:pr-12\">\n            <ScrollReveal>\n              <h3\n                className=\"mb-8 max-w-xl font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                Cool roof: a concrete answer\n                <span className=\"text-foreground/30\"> to the heat problem.</span>\n              </h3>\n              <p className=\"mb-8 max-w-lg font-body text-base leading-relaxed text-foreground/50\">\n                A light-colored surface sends solar radiation back before it penetrates the building. The roof heats up less, the top floors can breathe, and the substrate is subjected to less UV and thermal shock.\n              </p>\n              <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n            </ScrollReveal>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst SolutionSection = ({ page }: { page: RoofPageData }) => {\n  const [active, setActive] = useState(0);\n\n  return (\n    <section className=\"relative bg-white\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n        <ScrollReveal>\n          <div className=\"flex flex-col gap-8 pb-10 pt-16 lg:flex-row lg:items-end lg:gap-16 lg:pb-16 lg:pt-32\">\n            <div className=\"lg:w-2/3\">\n              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-muted-foreground\">\n                {page.solution.badge}\n              </p>\n              <h2\n                className=\"font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                <span className=\"text-foreground/30\">A system designed to </span>\n                reflect, protect and last.\n              </h2>\n            </div>\n            <div className=\"lg:w-1/3 lg:pb-1\">\n              <p className=\"font-body text-base leading-relaxed text-foreground/52 lg:text-right lg:text-lg\">\n                A primer, a resin and a top coat that guarantee your roof's performance over time.\n              </p>\n              <div className=\"mt-5 flex lg:justify-end\">\n                <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n              </div>\n            </div>\n          </div>\n        </ScrollReveal>\n\n        <div className=\"flex flex-col gap-12 pb-24 lg:flex-row lg:gap-16 lg:pb-32\">\n          <div className=\"overflow-hidden rounded-2xl shadow-[0_20px_60px_-15px_rgba(0,0,0,0.08)] lg:hidden\">\n            <img src={coolRoofLayers} alt=\"Cross-section of the Covalba cool roof system\" className=\"w-full\" loading=\"lazy\" />\n          </div>\n\n          <div className=\"lg:w-1/2 lg:sticky lg:top-[20vh] lg:self-start\">\n            {layerTitles.map((item, index) => {\n              const isOpen = active === index;\n              const colors = layerColors[index] ?? layerColors[0];\n              return (\n                <div key={item} className=\"border-b border-border last:border-b-0\">\n                  <button\n                    onClick={() => setActive(index)}\n                    className=\"group flex w-full items-center gap-4 py-6 text-left\"\n                  >\n                    <span\n                      className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full font-body text-[11px] font-bold transition-all duration-300 ${\n                        isOpen ? colors.pill : 'bg-muted text-muted-foreground'\n                      }`}\n                    >\n                      0{index + 1}\n                    </span>\n                    <h3\n                      className={`flex-1 font-satoshi text-xl font-bold transition-colors duration-300 lg:text-2xl ${\n                        isOpen ? 'text-foreground' : 'text-foreground/50 group-hover:text-foreground/80'\n                      }`}\n                    >\n                      {item}\n                    </h3>\n                    <ChevronDown\n                      className={`h-5 w-5 shrink-0 transition-all duration-300 ${\n                        isOpen ? `${colors.chevron} rotate-180` : 'text-foreground/25'\n                      }`}\n                      strokeWidth={2}\n                    />\n                  </button>\n\n                  <div\n                    className={`grid transition-all duration-500 ${\n                      isOpen ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'\n                    }`}\n                  >\n                    <div className=\"overflow-hidden\">\n                      <div className=\"pb-8 pl-6 lg:pl-12\">\n                        <p className=\"mb-6 font-body text-base leading-relaxed text-foreground/65 lg:text-lg\">\n                          {getLayerDescription(index)}\n                        </p>\n                        <div className=\"flex items-start gap-3 rounded-xl bg-foreground/[0.03] p-4\">\n                          <img\n                            src=\"/maxime-bourassin.jpg\"\n                            alt=\"Maxime Bourassin, founder of Covalba\"\n                            className=\"mt-0.5 h-9 w-9 shrink-0 rounded-full object-cover\"\n                          />\n                          <div>\n                            <p className=\"font-body text-[15px] italic leading-relaxed text-foreground/65 lg:text-base\">\n                              \"{getLayerExpert(index)}\"\n                            </p>\n                            <p className=\"mt-2 font-body text-xs font-semibold text-foreground/50\">\n                              Maxime Bourassin, founder\n                            </p>\n                          </div>\n                        </div>\n                        {index === 0 && page.solution.note && (\n                          <p className=\"mt-4 rounded-xl bg-primary/5 p-4 font-body text-sm leading-relaxed text-foreground/62\">\n                            {page.solution.note}\n                          </p>\n                        )}\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              );\n            })}\n          </div>\n\n          <div className=\"hidden lg:block lg:w-1/2\">\n            <div className=\"relative overflow-hidden rounded-2xl shadow-[0_30px_80px_-15px_rgba(0,0,0,0.08)]\">\n              <img src={coolRoofLayers} alt=\"Cross-section of the Covalba cool roof system\" className=\"w-full\" loading=\"lazy\" />\n              {layerTitles.map((item, index) => {\n                const pos = layerPositions[index];\n                const colors = layerColors[index] ?? layerColors[0];\n                const isActive = active === index;\n                if (!pos) return null;\n\n                return (\n                  <div key={item} className=\"absolute\" style={{ top: pos.top, left: pos.left }}>\n                    <div\n                      className={`absolute -translate-x-1/2 -translate-y-1/2 transition-all duration-500 ${\n                        isActive ? 'scale-125' : 'scale-75 opacity-40'\n                      }`}\n                    >\n                      <div\n                        className={`h-3.5 w-3.5 rounded-full ${\n                          isActive ? colors.dot : 'bg-foreground/20'\n                        } shadow-lg ring-2 ring-white transition-colors duration-500`}\n                      />\n                      {isActive && (\n                        <div className={`absolute inset-0 h-3.5 w-3.5 animate-ping rounded-full ${colors.dot} opacity-30`} />\n                      )}\n                    </div>\n                    <div\n                      className={`pointer-events-none absolute right-5 top-1/2 -translate-y-1/2 transition-all duration-500 ${\n                        isActive ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-0'\n                      }`}\n                    >\n                      <span className={`whitespace-nowrap rounded-full px-4 py-1.5 font-body text-[13px] font-bold shadow-lg ${colors.pill}`}>\n                        {item}\n                      </span>\n                    </div>\n                  </div>\n                );\n              })}\n            </div>\n          </div>\n        </div>\n      </div>\n    </section>\n  );\n};\n\nconst BenefitsSection = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-white py-20 lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mb-10 grid gap-8 lg:grid-cols-[0.82fr_1.18fr] lg:items-end lg:gap-14\">\n          <div>\n            <Label>{page.benefits.badge}</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n              {page.benefits.title}\n            </h2>\n          </div>\n          <p className=\"max-w-xl border-l border-primary/30 pl-6 font-body text-base leading-relaxed text-foreground/62 lg:text-lg\">\n            {page.benefits.featured}\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal stagger>\n        <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n          {page.benefits.cards.map((benefit, index) => {\n            const Icon = cardIcons[index] ?? Sparkles;\n            return (\n              <article\n                key={benefit.title}\n                className=\"group overflow-hidden rounded-2xl bg-cream ring-1 ring-foreground/[0.05] transition-all duration-500 hover:-translate-y-1 hover:shadow-[0_24px_70px_-48px_hsl(var(--navy)/0.45)]\"\n              >\n                <div className=\"relative aspect-[4/3] overflow-hidden bg-foreground/[0.04]\">\n                  <img\n                    src={getBenefitImage(page, benefit, index)}\n                    alt=\"\"\n                    className=\"h-full w-full object-cover transition-transform duration-700 group-hover:scale-[1.035]\"\n                    loading=\"lazy\"\n                    decoding=\"async\"\n                  />\n                  <div className=\"absolute inset-x-0 bottom-0 h-20 bg-gradient-to-t from-cream to-transparent\" />\n                </div>\n                <div className=\"p-5 lg:p-6\">\n                  <div className=\"mb-4 flex h-10 w-10 items-center justify-center rounded-xl bg-white text-primary shadow-[0_12px_30px_-24px_hsl(var(--navy)/0.35)]\">\n                    <Icon className=\"h-5 w-5\" strokeWidth={1.7} />\n                  </div>\n                  <h3 className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{benefit.title}</h3>\n                  <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/62\">{benefit.text}</p>\n                </div>\n              </article>\n            );\n          })}\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst RoiOptionCards = ({\n  page,\n  surface,\n  treatmentRange,\n}: {\n  page: RoofPageData;\n  surface: number;\n  treatmentRange: ReturnType<typeof getTreatmentRange>;\n}) => {\n  if (!page.roi.comparison) {\n    return page.roi.afterComparison ? (\n      <article className=\"rounded-2xl bg-white p-6 ring-1 ring-foreground/[0.06] lg:p-8\">\n        <p className=\"font-body text-base leading-relaxed text-foreground/66 lg:text-lg\">\n          {page.roi.afterComparison}\n        </p>\n      </article>\n    ) : null;\n  }\n\n  const options = page.roi.comparison.headers.slice(1);\n  const rows = page.roi.comparison.rows;\n  const investment = rows.find((row) => row[0].toLowerCase().includes('investment'));\n  const duration = rows.find((row) => {\n    const key = row[0].toLowerCase();\n    return key.includes('life') || key.includes('durat') || key.includes('lifespan');\n  });\n  const activity = rows.find((row) => {\n    const key = row[0].toLowerCase();\n    return key.includes('activity') || key.includes('operation');\n  });\n  const treatmentMidpoint = (treatmentRange.min + treatmentRange.max) / 2;\n\n  return (\n    <div className=\"grid gap-4 lg:grid-cols-3\">\n      {options.map((option, optionIndex) => {\n        const isCovalba = optionIndex === 0;\n        const rawInvestment = investment?.[optionIndex + 1] ?? '';\n        const parsedPrice = parsePricePerM2(rawInvestment);\n        const pricePerM2 = parsedPrice ?? getFallbackPricePerM2(option, optionIndex, treatmentMidpoint);\n        const dynamicInvestment = pricePerM2 ? `~${formatCurrency(surface * pricePerM2)}` : displayCell(rawInvestment);\n\n        return (\n          <article\n            key={option}\n            className={`rounded-2xl p-6 ring-1 ${\n              isCovalba\n                ? 'bg-[#142333] text-white ring-transparent'\n                : 'bg-white text-foreground ring-foreground/[0.06]'\n            }`}\n          >\n            <div className=\"mb-6 flex items-start justify-between gap-4\">\n              <h3 className=\"font-satoshi text-xl font-black leading-tight\">{option}</h3>\n              {isCovalba && (\n                <span className=\"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-teal-vivid/15 text-teal-vivid\">\n                  <Check className=\"h-4 w-4\" strokeWidth={2.4} />\n                </span>\n              )}\n            </div>\n            <p className={`font-satoshi text-3xl font-black leading-none ${isCovalba ? 'text-teal-vivid' : 'text-terracotta'}`}>\n              {dynamicInvestment}\n            </p>\n            {pricePerM2 && (\n              <p className={`mt-2 font-body text-xs font-semibold ${isCovalba ? 'text-white/45' : 'text-foreground/40'}`}>\n                basis {new Intl.NumberFormat('en-US').format(surface)} m² · {pricePerM2.toLocaleString('en-US')} €/m²\n              </p>\n            )}\n            <dl className=\"mt-6 space-y-3 border-t border-current/10 pt-5\">\n              {[duration, activity].filter(Boolean).map((row) => (\n                <div key={row![0]} className=\"grid grid-cols-[0.75fr_1fr] gap-4\">\n                  <dt className={`font-body text-xs font-bold uppercase tracking-[0.14em] ${isCovalba ? 'text-white/38' : 'text-foreground/35'}`}>\n                    {displayCell(row![0])}\n                  </dt>\n                  <dd className={`font-body text-sm leading-relaxed ${isCovalba ? 'text-white/74' : 'text-foreground/62'}`}>\n                    {displayCell(row![optionIndex + 1])}\n                  </dd>\n                </div>\n              ))}\n            </dl>\n          </article>\n        );\n      })}\n    </div>\n  );\n};\n\nconst ROICalculator = ({\n  page,\n  surface,\n  selectedSituation,\n  treatmentRange,\n  onSurfaceChange,\n  onSituationChange,\n}: {\n  page: RoofPageData;\n  surface: number;\n  selectedSituation: string;\n  treatmentRange: ReturnType<typeof getTreatmentRange>;\n  onSurfaceChange: (surface: number) => void;\n  onSituationChange: (situation: string) => void;\n}) => {\n  const treatmentLow = surface * treatmentRange.min;\n  const treatmentHigh = surface * treatmentRange.max;\n  const referencePrice = selectedSituation.toLowerCase().includes('insulation') ? 150 : 90;\n  const referenceCost = surface * referencePrice;\n  const potentialGap = Math.max(referenceCost - treatmentHigh, 0);\n\n  return (\n    <div className=\"mb-10 rounded-[2rem] bg-[#142333] p-5 text-white shadow-[0_34px_90px_-64px_hsl(var(--navy)/0.65)] lg:p-8\">\n      <div className=\"grid gap-7 lg:grid-cols-[0.88fr_1.12fr] lg:items-end\">\n        <div>\n          <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-teal-vivid/70\">\n            Indicative calculator\n          </p>\n          <h3 className=\"mt-4 max-w-lg font-satoshi text-2xl font-black leading-tight lg:text-3xl\">\n            Adjust the surface and the condition of the roof.\n          </h3>\n          <p className=\"mt-3 max-w-lg font-body text-sm leading-relaxed text-white/52\">\n            Default: 5,000 m², a typical surface for a warehouse or industrial building to treat.\n          </p>\n        </div>\n\n        <div className=\"grid gap-3 sm:grid-cols-[0.72fr_1.28fr]\">\n          <label className=\"block\">\n            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n              Surface\n            </span>\n            <input\n              type=\"number\"\n              min={100}\n              step={100}\n              value={surface}\n              onChange={(event) => onSurfaceChange(Math.max(100, Number(event.target.value) || 100))}\n              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n              aria-label=\"Surface to treat in square meters\"\n            />\n          </label>\n          <label className=\"block\">\n            <span className=\"mb-2 block font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/40\">\n              Situation\n            </span>\n            <select\n              value={selectedSituation}\n              onChange={(event) => onSituationChange(event.target.value)}\n              className=\"h-12 w-full rounded-xl border border-white/10 bg-white/[0.07] px-4 font-body text-sm font-semibold text-white outline-none transition focus:border-teal-vivid/50 focus:ring-2 focus:ring-teal-vivid/20\"\n              aria-label=\"Roof situation\"\n            >\n              {page.roi.situation.rows.map((row) => (\n                <option key={row[0]} value={row[0]} className=\"bg-card text-foreground\">\n                  {row[0]}\n                </option>\n              ))}\n            </select>\n          </label>\n        </div>\n      </div>\n\n      <div className=\"mt-7 grid gap-3 md:grid-cols-3\">\n        {[\n          {\n            label: treatmentRange.label,\n            value: `${formatCurrency(treatmentLow)} - ${formatCurrency(treatmentHigh)}`,\n            detail: `${treatmentRange.min}-${treatmentRange.max} €/m² depending on preparation`,\n            valueClass: 'text-teal-vivid',\n          },\n          {\n            label: 'Reference refurbishment',\n            value: `~${formatCurrency(referenceCost)}`,\n            detail: `${referencePrice} €/m², order of magnitude for comparison`,\n            valueClass: 'text-terracotta',\n          },\n          {\n            label: 'Potential gap',\n            value: `up to ${formatCurrency(potentialGap)}`,\n            detail: 'Heavy investment avoided or deferred if the substrate is viable',\n            valueClass: 'text-teal-vivid',\n          },\n        ].map((item) => (\n          <article key={item.label} className=\"rounded-2xl bg-white/[0.07] p-5 ring-1 ring-white/10\">\n            <p className=\"font-body text-[11px] font-bold uppercase tracking-[0.16em] text-white/38\">{item.label}</p>\n            <p className={`mt-3 font-satoshi text-2xl font-black leading-tight ${item.valueClass}`}>{item.value}</p>\n            <p className=\"mt-2 font-body text-xs leading-relaxed text-white/45\">{item.detail}</p>\n          </article>\n        ))}\n      </div>\n      <p className=\"mt-4 font-body text-xs leading-relaxed text-white/38\">\n        Indicative estimate: the study adjusts the price based on access, preparation, detailing points and the actual condition of the substrate.\n      </p>\n    </div>\n  );\n};\n\nconst getSituationImage = (recommendation: string) => {\n  const lower = recommendation.toLowerCase();\n  if (lower.includes('covametal')) return covaMetalProduct;\n  if (lower.includes('covaseal')) return covaSealProduct;\n  if (lower.includes('cova')) return covaTherm20Product;\n  return covaTherm8Product;\n};\n\nconst SituationCards = ({ rows }: { rows: string[][] }) => (\n  <div className=\"grid gap-4 md:grid-cols-2\">\n    {rows.map((row) => (\n      <article key={row[0]} className=\"grid gap-4 rounded-2xl bg-white p-4 ring-1 ring-foreground/[0.06] sm:grid-cols-[128px_1fr] lg:grid-cols-[190px_1fr] lg:gap-5 lg:p-5\">\n        <div className=\"flex aspect-[4/3] items-center justify-center rounded-xl bg-gradient-to-br from-cream to-white p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.75),_0_18px_44px_-34px_hsl(var(--navy)/0.45)] ring-1 ring-foreground/[0.04] sm:aspect-square lg:p-6\">\n          <img src={getSituationImage(row[1])} alt=\"\" className=\"max-h-full max-w-full object-contain\" loading=\"lazy\" decoding=\"async\" />\n        </div>\n        <div className=\"self-center\">\n          <p className=\"font-satoshi text-lg font-bold leading-tight text-foreground\">{row[0]}</p>\n          <p className=\"mt-2 font-body text-sm font-bold text-primary\">{row[1]}</p>\n          <p className=\"mt-2 font-body text-sm leading-relaxed text-foreground/58\">{row[2]}</p>\n        </div>\n      </article>\n    ))}\n  </div>\n);\n\nconst ROISection = ({ page }: { page: RoofPageData }) => {\n  const [surface, setSurface] = useState(5000);\n  const [selectedSituation, setSelectedSituation] = useState(page.roi.situation.rows[0]?.[0] ?? '');\n  const treatmentRange = getTreatmentRange(page, selectedSituation);\n\n  return (\n    <section className=\"bg-cream py-20 lg:py-32\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto mb-12 max-w-3xl text-center lg:mb-16\">\n          <div className=\"flex justify-center\">\n            <Label>{page.roi.badge}</Label>\n          </div>\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-[3rem]\">\n            {page.roi.title}\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-foreground/58 lg:text-lg\">\n            {page.roi.intro}\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mb-12 grid gap-4 md:grid-cols-3\">\n          {[\n            ['20-25 €/m²', 'to treat and cool down'],\n            ['8-10 years', 'of refurbishment deferred'],\n            ['0 downtime', 'during the works'],\n          ].map(([value, label]) => (\n            <article key={value} className=\"rounded-2xl bg-white p-6 text-center ring-1 ring-foreground/[0.06]\">\n              <p className=\"font-satoshi text-4xl font-black leading-none text-primary\">{value}</p>\n              <p className=\"mt-3 font-body text-sm font-semibold text-foreground/52\">{label}</p>\n            </article>\n          ))}\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mb-8 max-w-2xl\">\n          <div>\n            <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n              Compare the options\n            </p>\n            <h3 className=\"mt-4 font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n              A quick read on the budget.\n            </h3>\n          </div>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <ROICalculator\n          page={page}\n          surface={surface}\n          selectedSituation={selectedSituation}\n          treatmentRange={treatmentRange}\n          onSurfaceChange={setSurface}\n          onSituationChange={setSelectedSituation}\n        />\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <RoiOptionCards page={page} surface={surface} treatmentRange={treatmentRange} />\n        {page.roi.comparison && page.roi.afterComparison && (\n          <p className=\"mt-5 max-w-3xl font-body text-sm leading-relaxed text-foreground/52 lg:text-base\">\n            {page.roi.afterComparison}\n          </p>\n        )}\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mt-16 rounded-[2rem] bg-white p-5 ring-1 ring-foreground/[0.06] lg:mt-20 lg:p-8\">\n          <div className=\"mb-6 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between\">\n            <div>\n              <p className=\"font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/55\">\n                Decide on the actual condition\n              </p>\n              <h3 className=\"mt-4 max-w-xl font-satoshi text-2xl font-black leading-tight text-foreground lg:text-3xl\">\n                Which solution for your roof?\n              </h3>\n            </div>\n            <PrimaryCTA label={page.roi.cta} href={CTA_HREF} />\n          </div>\n          <SituationCards rows={page.roi.situation.rows} />\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n  );\n};\n\nconst DiagnosticCTA = ({ title, text }: { title: string; text: string }) => (\n  <section className=\"relative overflow-hidden bg-[#182A3A] py-14 text-white lg:py-20\">\n    <div\n      aria-hidden=\"true\"\n      className=\"absolute inset-0 opacity-35\"\n      style={{\n        backgroundImage:\n          'radial-gradient(circle at 16% 20%, rgba(83, 207, 166, 0.22), transparent 28%), linear-gradient(115deg, rgba(255,255,255,0.05) 0 1px, transparent 1px 120px)',\n      }}\n    />\n    <div className=\"container relative mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto grid max-w-5xl gap-7 lg:grid-cols-[1fr_auto] lg:items-center\">\n          <div>\n            <h2 className=\"font-satoshi text-2xl font-black leading-tight lg:text-4xl\">{title}</h2>\n            <p className=\"mt-3 max-w-2xl font-body text-sm leading-relaxed text-white/68 lg:text-base\">{text}</p>\n          </div>\n          <div className=\"flex flex-col gap-3 sm:flex-row lg:justify-end\">\n            <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n          </div>\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst MethodSection = ({ page }: { page: RoofPageData }) => {\n  const [videoOpen, setVideoOpen] = useState(false);\n\n  return (\n    <section className=\"bg-white py-16 lg:py-32\">\n      <div className=\"container mx-auto px-4 lg:px-8\">\n        <div className=\"flex flex-col items-start gap-12 lg:flex-row lg:gap-20\">\n          <div className=\"lg:sticky lg:top-[120px] lg:w-[40%]\">\n            <ScrollReveal>\n              <p className=\"mb-6 font-body text-[10px] font-semibold uppercase tracking-[0.25em] text-primary/50\">\n                The process\n              </p>\n              <h2\n                className=\"mb-6 font-satoshi text-3xl font-black text-foreground sm:text-4xl lg:text-[3rem]\"\n                style={{ letterSpacing: '-0.03em', lineHeight: 1.05 }}\n              >\n                4 steps. Zero interruption.\n                <span className=\"text-foreground/30\"> Zero risk.</span>\n              </h2>\n              <p className=\"mb-8 font-body text-base leading-relaxed text-muted-foreground lg:text-lg\">\n                {page.method.reassurance}\n              </p>\n              <button\n                onClick={() => setVideoOpen(true)}\n                className=\"group flex items-center gap-3 text-primary transition-colors hover:text-primary\"\n              >\n                <span className=\"flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 transition-colors group-hover:bg-primary/20\">\n                  <Play className=\"h-5 w-5\" />\n                </span>\n                <span className=\"font-body text-sm font-semibold\">Watch a project in 2 minutes</span>\n              </button>\n            </ScrollReveal>\n          </div>\n\n          <div className=\"relative lg:w-[60%]\">\n            <div className=\"absolute bottom-4 left-5 top-4 w-px bg-gradient-to-b from-primary/25 via-primary/10 to-transparent lg:left-6\" />\n            <div className=\"flex flex-col\">\n              {page.method.steps.map((step, index) => {\n                const Icon = methodIcons[index] ?? CheckCircle2;\n                return (\n                  <ScrollReveal key={step.title}>\n                    <div className=\"group relative flex items-start gap-5 py-8 lg:gap-7 lg:py-10\">\n                      <span className=\"relative z-10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full border border-primary/20 bg-primary/10 transition-colors duration-500 group-hover:bg-primary/15 lg:h-12 lg:w-12\">\n                        <Icon className=\"h-4 w-4 text-primary lg:h-5 lg:w-5\" />\n                      </span>\n                      <div className=\"pt-1\">\n                        <span className=\"mb-2 block font-body text-xs font-bold uppercase tracking-widest text-primary/50\">\n                          Step 0{index + 1}\n                        </span>\n                        <h3 className=\"mb-2 font-satoshi text-xl font-bold text-foreground lg:text-2xl\">\n                          {step.title}\n                        </h3>\n                        <p className=\"max-w-md font-body text-sm leading-relaxed text-muted-foreground lg:text-base\">\n                          {step.text}\n                        </p>\n                      </div>\n                    </div>\n                    {index < page.method.steps.length - 1 && <div className=\"ml-5 h-px bg-border lg:ml-6\" />}\n                  </ScrollReveal>\n                );\n              })}\n            </div>\n\n            <ScrollReveal>\n              <div className=\"ml-16 mt-4 lg:ml-20\">\n                <p className=\"font-body text-xs font-medium text-muted-foreground/50\">\n                  Average duration: 3 days · No interruption of activity\n                </p>\n              </div>\n            </ScrollReveal>\n          </div>\n        </div>\n      </div>\n\n      <Dialog open={videoOpen} onOpenChange={setVideoOpen}>\n        <DialogContent className=\"max-w-4xl overflow-hidden border-none bg-black p-0\">\n          <div className=\"aspect-video\">\n            <iframe\n              src=\"https://www.youtube.com/embed/9HOB5OVH0Ac?autoplay=1\"\n              className=\"h-full w-full\"\n              allow=\"autoplay; encrypted-media\"\n              allowFullScreen\n              title=\"Covalba Cool Roof, a project in 2 minutes\"\n            />\n          </div>\n        </DialogContent>\n      </Dialog>\n    </section>\n  );\n};\n\nconst ProofSection = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-[#192A3A] py-20 text-white lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mb-12 grid gap-8 lg:grid-cols-[0.9fr_1.1fr] lg:items-end\">\n          <div>\n            <Label light>{page.proof.badge}</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight sm:text-4xl lg:text-5xl\">{page.proof.title}</h2>\n          </div>\n          <p className=\"max-w-xl font-body text-base leading-relaxed text-white/62\">\n            Field figures to be confirmed project by project, used here as performance benchmarks.\n          </p>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal stagger>\n        <div className=\"grid gap-4 md:grid-cols-3\">\n          {page.proof.figures.map((figure, index) => {\n            const Icon = proofIcons[index] ?? BadgeCheck;\n            return (\n              <article key={figure.value} className=\"rounded-2xl border border-white/10 bg-white/[0.045] p-7\">\n                <Icon className=\"mb-6 h-6 w-6 text-teal-vivid\" strokeWidth={1.7} />\n                <p className=\"font-satoshi text-3xl font-black leading-none text-white lg:text-4xl\">{figure.value}</p>\n                <p className=\"mt-3 font-body text-sm leading-relaxed text-white/60\">{figure.label}</p>\n              </article>\n            );\n          })}\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <div className=\"mt-14 border-t border-white/10 pt-10\">\n          <p className=\"mb-8 text-center font-body text-[10px] font-bold uppercase tracking-[0.22em] text-white/42\">\n            Client references\n          </p>\n          <div className=\"flex flex-wrap items-center justify-center gap-x-10 gap-y-7 lg:gap-x-14\">\n            {logos.map((logo) => (\n              <span key={logo.name} className=\"flex h-8 w-28 items-center justify-center\">\n                <img\n                  src={logo.url}\n                  alt={logo.name}\n                  className=\"max-h-7 max-w-full object-contain opacity-45 transition-opacity duration-300 hover:opacity-80\"\n                  style={{ filter: 'brightness(0) invert(1)' }}\n                  loading=\"lazy\"\n                />\n              </span>\n            ))}\n          </div>\n        </div>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst FAQSection = ({ faq }: { faq: RoofPageData['faq'] }) => (\n  <section className=\"bg-cream py-20 lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <div className=\"grid gap-10 lg:grid-cols-[0.85fr_1.15fr] lg:gap-16\">\n        <ScrollReveal>\n          <div className=\"lg:sticky lg:top-24\">\n            <Label>FAQ</Label>\n            <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground sm:text-4xl lg:text-5xl\">\n              Your questions,\n              <br />\n              <span className=\"text-foreground/40\">our answers.</span>\n            </h2>\n            <a href=\"#contact\" className=\"group mt-8 inline-flex items-center gap-2 font-body text-sm font-semibold text-primary transition-all hover:gap-3\">\n              Another question? Let's talk\n              <ArrowRight className=\"h-4 w-4\" />\n            </a>\n          </div>\n        </ScrollReveal>\n\n        <ScrollReveal>\n          <Accordion type=\"single\" collapsible className=\"space-y-3\">\n            {faq.map((item, index) => (\n              <AccordionItem\n                key={item.q}\n                value={`roof-faq-${index}`}\n                className=\"rounded-xl border border-foreground/[0.06] bg-white px-5 shadow-[0_12px_34px_rgba(24,42,58,0.04)] data-[state=open]:border-primary/20 lg:px-6\"\n              >\n                <AccordionTrigger className=\"py-5 text-left font-satoshi text-base font-bold text-foreground hover:no-underline lg:text-[17px]\">\n                  {item.q}\n                </AccordionTrigger>\n                <AccordionContent className=\"pb-5 font-body text-[14.5px] leading-relaxed text-foreground/67 lg:text-[15px]\">\n                  {item.a}\n                </AccordionContent>\n              </AccordionItem>\n            ))}\n          </Accordion>\n        </ScrollReveal>\n      </div>\n    </div>\n  </section>\n);\n\nconst FinalCTA = ({ page }: { page: RoofPageData }) => (\n  <section id=\"contact\" className=\"overflow-hidden bg-[#182A3A] py-20 text-white lg:py-32\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <div className=\"mx-auto max-w-4xl text-center\">\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-white sm:text-4xl lg:text-5xl\">\n            {page.finalCta.title}\n          </h2>\n          <p className=\"mx-auto mt-5 max-w-2xl font-body text-base leading-relaxed text-white/72 lg:text-lg\">\n            {page.finalCta.text}\n          </p>\n          <div className=\"mt-9 flex flex-col gap-3 sm:flex-row sm:justify-center\">\n            <PrimaryCTA label=\"Request a quote\" href={CTA_HREF} />\n          </div>\n        </div>\n      </ScrollReveal>\n\n      <ScrollReveal>\n        <ul className=\"mx-auto mt-12 grid max-w-3xl gap-3 sm:grid-cols-3\">\n          {page.finalCta.reassurances.map((item) => (\n            <li key={item} className=\"flex items-center justify-center gap-2.5 rounded-lg border border-white/[0.12] bg-white/[0.045] px-4 py-3\">\n              <CalendarCheck className=\"h-4 w-4 shrink-0 text-teal-vivid\" strokeWidth={1.8} />\n              <span className=\"font-body text-sm text-white/[0.76]\">{item}</span>\n            </li>\n          ))}\n        </ul>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst SEOContent = ({ page }: { page: RoofPageData }) => (\n  <section className=\"bg-white py-20 lg:py-28\">\n    <div className=\"container mx-auto px-4 lg:px-8\">\n      <ScrollReveal>\n        <article className=\"mx-auto max-w-3xl\">\n          <p className=\"mb-5 font-body text-[10px] font-bold uppercase tracking-[0.25em] text-primary/55\">\n            Substrate guide\n          </p>\n          <h2 className=\"font-satoshi text-3xl font-black leading-tight text-foreground lg:text-4xl\">\n            Everything you need to know about {page.eyebrow.toLowerCase()}.\n          </h2>\n          <p className=\"mt-6 font-body text-base leading-relaxed text-foreground/68 lg:text-lg\">\n            {page.seo.intro}\n          </p>\n\n          <div className=\"mt-10 space-y-9\">\n            {page.seo.sections.map((section) => (\n              <section key={section.title}>\n                <h3 className=\"font-satoshi text-2xl font-bold leading-tight text-foreground\">{section.title}</h3>\n                <p className=\"mt-3 font-body text-base leading-relaxed text-foreground/66\">{section.text}</p>\n              </section>\n            ))}\n          </div>\n\n          <div className=\"mt-12 rounded-2xl bg-cream p-6 ring-1 ring-foreground/[0.05]\">\n            <h3 className=\"font-satoshi text-lg font-bold text-foreground\">Sources</h3>\n            <ul className=\"mt-4 space-y-2\">\n              {page.seo.sources.map((source) => (\n                <li key={source.href}>\n                  <a href={source.href} className=\"font-body text-sm leading-relaxed text-primary underline-offset-4 hover:underline\" target=\"_blank\" rel=\"noreferrer\">\n                    {source.label}\n                  </a>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </article>\n      </ScrollReveal>\n    </div>\n  </section>\n);\n\nconst buildFaqSchema = (page: RoofPageData) => ({\n  '@context': 'https://schema.org',\n  '@type': 'FAQPage',\n  mainEntity: page.faq.map((item) => ({\n    '@type': 'Question',\n    name: item.q,\n    acceptedAnswer: {\n      '@type': 'Answer',\n      text: item.a,\n    },\n  })),\n});\n\nconst buildBreadcrumbSchema = (page: RoofPageData) => ({\n  '@context': 'https://schema.org',\n  '@type': 'BreadcrumbList',\n  itemListElement: [\n    { '@type': 'ListItem', position: 1, name: 'Home', item: `${SITE_URL}/en` },\n    { '@type': 'ListItem', position: 2, name: 'Roofs', item: `${SITE_URL}/en` },\n    { '@type': 'ListItem', position: 3, name: page.breadcrumb, item: page.canonical },\n  ],\n});\n\nconst RoofPageTemplateEN = ({ page }: RoofPageTemplateENProps) => {\n  usePageMeta({\n    title: page.title,\n    description: page.description,\n    canonical: page.canonical,\n  });\n  useJsonLd(`roof-en-${page.slug}-faq`, buildFaqSchema(page));\n  useJsonLd(`roof-en-${page.slug}-breadcrumb`, buildBreadcrumbSchema(page));\n\n  return (\n    <>\n      <NavbarEN />\n      <main>\n        <RoofHero page={page} />\n        <ProblemSection page={page} />\n        <SolutionSection page={page} />\n        <BenefitsSection page={page} />\n        <ROISection page={page} />\n        <DiagnosticCTA title={page.midCta.title} text={page.midCta.text} />\n        <MethodSection page={page} />\n        <ProofSection page={page} />\n        <FAQSection faq={page.faq} />\n        <FinalCTA page={page} />\n        <SEOContent page={page} />\n      </main>\n      <FooterEN />\n      <StickyMobileCTAEN />\n    </>\n  );\n};\n\nexport default RoofPageTemplateEN;\n","structuredPatch":[],"originalFile":null,"userModified":false},"sourceToolAssistantUUID":"ac9aef3e-ac37-4eac-9f32-f22e4d914295","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"af871b56-eb69-4f6f-b4d1-e5f34048fcc0","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_0192JKUGMYDvGUXi7iTqoRs4","type":"message","role":"assistant","content":[{"type":"text","text":"Let me verify the EN chrome components have default exports and confirm `TextCard` is exported from roofPages (I imported it)."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":25241,"cache_read_input_tokens":97886,"cache_creation":{"ephemeral_5m_input_tokens":25241,"ephemeral_1h_input_tokens":0},"output_tokens":7,"service_tier":"standard","inference_geo":"not_available"},"diagnostics":null},"requestId":"req_011CcHKjknJMLXugGkivHcmU","attributionAgent":"general-purpose","type":"assistant","uuid":"17142e80-52e9-400b-941d-c1c205b24ec2","timestamp":"2026-06-22T00:07:18.176Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"17142e80-52e9-400b-941d-c1c205b24ec2","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_0192JKUGMYDvGUXi7iTqoRs4","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Jz93YcEi5YfgcKgDspPTnM","name":"Bash","input":{"command":"cd /opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all\necho \"=== default exports ===\"; grep -n \"export default\" src/components/en/NavbarEN.tsx src/components/en/FooterEN.tsx src/components/en/StickyMobileCTAEN.tsx\necho \"=== TextCard export ===\"; grep -n \"export interface TextCard\\|export.*TextCard\" src/data/roofPages.ts\necho \"=== ScrollReveal export ===\"; grep -n \"export default\\|stagger\" src/components/ScrollReveal.tsx | head","description":"Verify exports and prop support"},"caller":{"type":"direct"}}],"stop_reason":"tool_use","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":25241,"cache_read_input_tokens":97886,"output_tokens":319,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":25241},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":319,"cache_read_input_tokens":97886,"cache_creation_input_tokens":25241,"cache_creation":{"ephemeral_5m_input_tokens":25241,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CcHKjknJMLXugGkivHcmU","attributionAgent":"general-purpose","type":"assistant","uuid":"37f1c411-d1c1-47dc-976c-e7948303f9c3","timestamp":"2026-06-22T00:07:20.356Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"37f1c411-d1c1-47dc-976c-e7948303f9c3","isSidechain":true,"promptId":"c0241083-27b1-42ff-b025-fa3ab7cc59f1","agentId":"acb7cb0381e40f6e1","type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Jz93YcEi5YfgcKgDspPTnM","type":"tool_result","content":"=== default exports ===\nsrc/components/en/StickyMobileCTAEN.tsx:28:export default StickyMobileCTAEN;\nsrc/components/en/FooterEN.tsx:77:export default FooterEN;\nsrc/components/en/NavbarEN.tsx:400:export default NavbarEN;\n=== TextCard export ===\n7:export interface TextCard {\n=== ScrollReveal export ===\n7:  stagger?: boolean;\n10:const ScrollReveal = ({ children, className = '', stagger = false }: ScrollRevealProps) => {\n16:      className={`${stagger ? 'stagger-children' : 'section-reveal'} ${inView ? 'in-view' : ''} ${className}`}\n23:export default ScrollReveal;","is_error":false}]},"uuid":"ab9cbc94-9d82-46cf-97d3-c9f4f20f32da","timestamp":"2026-06-22T00:07:20.479Z","toolUseResult":{"stdout":"=== default exports ===\nsrc/components/en/StickyMobileCTAEN.tsx:28:export default StickyMobileCTAEN;\nsrc/components/en/FooterEN.tsx:77:export default FooterEN;\nsrc/components/en/NavbarEN.tsx:400:export default NavbarEN;\n=== TextCard export ===\n7:export interface TextCard {\n=== ScrollReveal export ===\n7:  stagger?: boolean;\n10:const ScrollReveal = ({ children, className = '', stagger = false }: ScrollRevealProps) => {\n16:      className={`${stagger ? 'stagger-children' : 'section-reveal'} ${inView ? 'in-view' : ''} ${className}`}\n23:export default ScrollReveal;","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false},"sourceToolAssistantUUID":"37f1c411-d1c1-47dc-976c-e7948303f9c3","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
{"parentUuid":"ab9cbc94-9d82-46cf-97d3-c9f4f20f32da","isSidechain":true,"agentId":"acb7cb0381e40f6e1","message":{"model":"claude-opus-4-8","id":"msg_0173g4SJj6STAhGgujvRUFak","type":"message","role":"assistant","content":[{"type":"text","text":"All imports resolve correctly: `TextCard` and `RoofPageData` are both exported from `roofPages.ts`, all three EN chrome components have default exports, and `ScrollReveal` supports the `stagger` prop. The file is complete and faithful.\n\n## Report\n\n**File created:** `/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all/src/components/en/roof/RoofPageTemplateEN.tsx`\n\nFaithful copy of `RoofPageTemplate.tsx` — identical JSX structure, Tailwind classes, GSAP/ScrollReveal animations, state/logic, image & icon imports. `export default RoofPageTemplateEN` with the same `{ page }: { page: RoofPageData }` prop shape.\n\n**RoofPageData import used (exactly):**\n```ts\nimport { type RoofPageData, type TextCard } from '@/data/roofPages';\n```\n(`TextCard` is also re-used from the same module since the original `getBenefitImage` helper references it.)\n\n**EN chrome swapped in:** `NavbarEN`, `FooterEN`, `StickyMobileCTAEN` (default exports verified) replace the FR `Navbar/Footer/StickyMobileCTA`. SEO/meta unchanged: `usePageMeta` still derives `canonical` from `page.canonical`; JSON-LD keys namespaced `roof-en-{slug}-*` to avoid colliding with FR keys.\n\n**French chrome strings translated, grouped:**\n\n- *Hero form/chrome:* breadcrumb \"Accueil/Toitures\" → \"Home/Roofs\", \"Bénéfices clés\" → \"Key benefits\"; \"Estimez le gain possible\" → \"Estimate your potential savings\"; \"Réponse personnalisée sous 24h\"; select label + options (\"Bâtiment climatisé ?\" → \"Is the building air-conditioned?\", Oui/Non/Partiellement/À confirmer → Yes/No/Partially/To confirm); placeholders \"Surface toiture (m²)\", \"Email professionnel\" → \"Work email\"; \"Voir mon potentiel\" → \"See my potential\"; \"Sans engagement · Étude adaptée au support\"; success state (\"Base indicative pour … m²\", estimate refine line, \"Modifier ma demande\"); \"Ils nous font confiance\" → \"They trust us\".\n- *Problem section:* \"Le cool roof : une réponse concrète au problème de chaleur.\" + body paragraph; `getProblemTitle` map (5 titles + strong/muted splits) translated.\n- *Solution section:* \"Un système conçu pour réfléchir, protéger et durer.\"; primer/resin/topcoat helper text (`layerTitles`, `getLayerDescription`, `getLayerExpert`); founder alt/caption \"Maxime Bourassin, founder\".\n- *ROI section:* \"Calculateur indicatif\", calculator headings/labels (Surface, Situation, \"Default: 5,000 m²…\"), result tiles (\"Reference refurbishment\", \"Potential gap\", \"up to …\"), per-m² \"basis\" line, disclaimer; the 3 stat tiles (\"to treat and cool down\", \"of refurbishment deferred\", \"0 downtime\"); \"Compare the options / A quick read on the budget\"; \"Decide on the actual condition / Which solution for your roof?\".\n- *Method section:* \"The process\", \"4 steps. Zero interruption. Zero risk.\", \"Watch a project in 2 minutes\", \"Step 0X\", \"Average duration: 3 days · No interruption of activity\", iframe title.\n- *Proof section:* benchmark caption line, \"Client references\".\n- *FAQ:* \"FAQ\", \"Your questions, our answers.\", \"Another question? Let's talk\".\n- *Final CTA / SEO:* \"Request a quote\" CTAs (product pivot), \"Substrate guide\", \"Everything you need to know about …\", \"Sources\".\n- *Alt texts / aria-labels:* cool-roof cross-section alts, `supportVisuals` hot/cool labels (all 5 substrates), surface/situation aria-labels.\n- *Currency/number formatting:* `Intl.NumberFormat` locale `fr-FR` → `en-US` (metric `€`/`m²`/`°C`/`%` preserved).\n\n**Flags / decisions:**\n- **Links:** No EN diagnostic/contact route exists, so all hardcoded CTAs (`/diagnostic` in FR) point to `/en` via a single `CTA_HREF` constant. Breadcrumb `Accueil`→`/en`, breadcrumb schema items point to `${SITE_URL}/en`. Note: the FR template never actually renders `page.solution.cta` (product link) — confirmed unused — so no `/en/solutions/*` link is emitted by the template itself; those routes exist if you later wire it. No dead links.\n- **`getProblemTitle` (data-driven French→English heuristic):** its map keys are full FR sentences and won't match English `page.problem.title` values. I translated the keys to the expected EN titles so the strong/muted split still works *if* the EN data uses those exact strings; otherwise it cleanly falls back to rendering the full title with empty muted (no broken/French output). **Action for EN page data author:** match these title strings for the two-tone effect, or accept the single-tone fallback.\n- **Keyword-matching helpers** (`getTreatmentRange`, `getFallbackPricePerM2`, `getBenefitImage`, `ROICalculator` reference price, `RoiOptionCards` row lookups): FR keyword matches replaced with EN equivalents **plus** language-neutral `slug` matches (e.g. `membrane-bitumineuse`, `bac-acier`) as a robust anchor so image/price selection keeps working regardless of EN copy wording. The row lookups (investment/lifespan/activity) now match English column labels — **the EN comparison-table data should use headers/first-column labels containing \"investment\", \"life\"/\"lifespan\", \"activity\"** for the option cards to populate; otherwise those rows are simply omitted (graceful).\n- **No France-specific chrome** (CEE, décret, +33, Orléans, \"fabricant français\") existed as hardcoded strings in this template — nothing to strip. Any regulatory/asbestos note comes from `page.solution.note` (data), left untouched.\n- **Config fields left untouched** (passed through verbatim from `page.*`): all `title/description/canonical/h1/lead/badge/figures/faq/seo` etc.\n- No FR file modified; nothing built/run/committed."}],"stop_reason":"end_turn","stop_sequence":null,"stop_details":null,"usage":{"input_tokens":2,"cache_creation_input_tokens":579,"cache_read_input_tokens":123127,"output_tokens":2224,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":579},"inference_geo":"not_available","iterations":[{"input_tokens":2,"output_tokens":2224,"cache_read_input_tokens":123127,"cache_creation_input_tokens":579,"cache_creation":{"ephemeral_5m_input_tokens":579,"ephemeral_1h_input_tokens":0},"type":"message"}],"speed":"standard"},"diagnostics":null},"requestId":"req_011CcHKk7sZcAZ6fVUrUjyBh","attributionAgent":"general-purpose","type":"assistant","uuid":"836daf58-b2c0-418f-9b18-09f3ace497e4","timestamp":"2026-06-22T00:07:50.647Z","userType":"external","entrypoint":"cli","cwd":"/opt/projects/.covalba-refonte-worktrees/nico-feature-traduction-all","sessionId":"22d3abbc-6868-484e-8bb0-ceaa184d3a29","version":"2.1.181","gitBranch":"nico/feature-traduction-all","slug":"mutable-snacking-pearl"}
