{"id":16298,"date":"2025-10-31T08:39:23","date_gmt":"2025-10-31T13:39:23","guid":{"rendered":"https:\/\/collectionimmobiliere.com\/?page_id=16298"},"modified":"2026-01-12T13:03:08","modified_gmt":"2026-01-12T18:03:08","slug":"cost-estimator-prefab-home","status":"publish","type":"page","link":"https:\/\/collectionimmobiliere.com\/en\/simulateur-de-cout-pour-maison-prefabriquee\/","title":{"rendered":"Prefab Home Cost Estimator \ud83c\udfe1"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"16298\" class=\"elementor elementor-16298\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3ff63f7 e-flex e-con-boxed e-con e-parent\" data-id=\"3ff63f7\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b764e6b elementor-widget elementor-widget-html\" data-id=\"b764e6b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- Bloc d'introduction harmonis\u00e9 -->\r\n<style>\r\n  .simu-hero {\r\n    max-width: 1100px;\r\n    margin: 0 auto 28px;\r\n    background: #f8fbff;\r\n    border: 1px solid #e6eef7;\r\n    border-radius: 14px;\r\n    padding: 22px 24px;\r\n  }\r\n  .simu-hero .eyebrow {\r\n    display: inline-block;\r\n    font-size: 12px;\r\n    font-weight: 700;\r\n    letter-spacing: 0.06em;\r\n    text-transform: uppercase;\r\n    color: #2563eb;\r\n    background: #eaf1ff;\r\n    border: 1px solid #dbe7ff;\r\n    border-radius: 999px;\r\n    padding: 4px 10px;\r\n    margin-bottom: 10px;\r\n  }\r\n  .simu-hero p {\r\n    margin: 12px 0;\r\n    color: #334155;\r\n    font-size: 17px;\r\n    line-height: 1.6;\r\n  }\r\n  .simu-hero strong {\r\n    color: #0b1220;\r\n  }\r\n  .simu-hero .disclaimer {\r\n    margin-top: 14px;\r\n    font-size: 14px;\r\n    color: #667085;\r\n    border-top: 1px dashed #e6eef7;\r\n    padding-top: 12px;\r\n  }\r\n<\/style>\r\n\r\n<section class=\"simu-hero\" aria-label=\"Estimateur de co\u00fbts de maison pr\u00e9fabriqu\u00e9e\">\r\n  <span class=\"eyebrow\">Estimateur de prix<\/span>\r\n\r\n  <p>\r\n    Calculez en quelques secondes le <strong>co\u00fbt d\u2019une maison pr\u00e9fabriqu\u00e9e au Qu\u00e9bec<\/strong>.  \r\n    Notre <strong>simulateur en ligne<\/strong> estime le prix total selon la <strong>superficie<\/strong>, le <strong>type de pr\u00e9fabrication<\/strong> (modulaire ou en kit) et le <strong>type de fondation<\/strong>.\r\n  <\/p>\r\n\r\n  <p>\r\n    L\u2019outil d\u00e9compose automatiquement les postes cl\u00e9s \u2014 <strong>excavation<\/strong>, <strong>finitions<\/strong>, <strong>raccordements<\/strong> et <strong>impr\u00e9vus<\/strong> \u2014 selon les standards du march\u00e9 qu\u00e9b\u00e9cois.  \r\n   \r\n  <\/p>\r\n  \r\n<p>Vous pouvez ajuster les chiffres dans les champs et onglets avanc\u00e9s pour cr\u00e9er vos propres simulations. Dans ces champs, entrez vos valeurs au format 5-15 ou 5,5-15 afin d\u2019indiquer les plages de prix \u00e0 calculer.<\/p>\r\n\r\n\r\n     \r\n\r\n  <p class=\"disclaimer\">\r\n    \ud83d\udca1 Estimation \u00e0 titre <strong>indicatif<\/strong> : les co\u00fbts peuvent varier selon la r\u00e9gion, le terrain et les finitions.<br>\r\n    \ud83d\udcec Suggestions? \u00c9crivez-nous \u00e0 <a href=\"mailto:info@collectionimmobiliere.com\">info@collectionimmobiliere.com<\/a>.\r\n  <\/p>\r\n<\/section>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-55eb901 simu-eq e-flex e-con-boxed e-con e-parent\" data-id=\"55eb901\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-8535568 e-con-full e-flex e-con e-child\" data-id=\"8535568\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-fe6cfcc simu-card elementor-widget__width-initial elementor-widget elementor-widget-jet-engine-booking-form\" data-id=\"fe6cfcc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"jet-engine-booking-form.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<form  class=\"jet-form layout-column submit-type-reload\" action=\"\/en\/wp-json\/wp\/v2\/pages\/16298?jet_engine_action=book&#038;nocache=1776967932\" method=\"POST\" data-form-id=\"16570\"><input class=\"jet-form__field hidden-field\" type=\"hidden\" name=\"_jet_engine_booking_form_id\" value=\"16570\" data-field-name=\"_jet_engine_booking_form_id\"><input class=\"jet-form__field hidden-field\" type=\"hidden\" name=\"_jet_engine_refer\" value=\"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/pages\/16298\" data-field-name=\"_jet_engine_refer\"><div  class=\"jet-form-row jet-form-row--first-visible\"><div class=\"jet-form-col jet-form-col-12  field-type-heading section-title jet-form-field-container\" data-field=\"heading\" data-conditional=\"false\"><div class=\"jet-form__heading\">\n\t<span class=\"jet-form__label-text\" ><strong>1) Param\u00e8tres de base<\/strong><\/span>\n\t<\/div><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-6  field-type-number subfield-half adv-half jet-form-field-container\" data-field=\"area\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Superficie (pi\u00b2)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input type=\"number\" class=\"jet-form__field text-field\" placeholder=\"Ex. 1000\" value=\"1000\" required=\"required\" min=\"600\" max=\"3000\" step=\"10\" name=\"area\" data-field-name=\"area\" id=\"area\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Entrez la superficie totale de la maison ou du chalet<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-6  field-type-select subfield-half adv-half jet-form-field-container\" data-field=\"region_factor\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Ajustement r\u00e9gional<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><select  required=\"required\" name=\"region_factor\" data-field-name=\"region_factor\" id=\"region_factor\" data-default-val=\"1\" class=\"jet-form__field select-field\"><option value=\"\" >Choisir une option<\/option><option value=\"0.95\" >R\u00e9gion moins co\u00fbteuse (\u22125 %)<\/option><option value=\"1\"  selected='selected'>Standard (\u00b10 %)<\/option><option value=\"1.10\" >Zone plus co\u00fbteuse (+10 %)<\/option><\/select><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Ajuste le co\u00fbt total selon le march\u00e9 local ou la r\u00e9gion.<\/span><\/small><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-6  field-type-number subfield-half adv-half jet-form-field-container\" data-field=\"module_price\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Prix du module ou du kit (CAD)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input type=\"number\" class=\"jet-form__field text-field\" placeholder=\"Ex. 180000\" value=\"180000\" required=\"required\" min=\"50000\" max=\"1000000\" step=\"1000\" name=\"module_price\" data-field-name=\"module_price\" id=\"module_price\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Indique le prix total du module ou du kit pr\u00e9fabriqu\u00e9.<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-6  field-type-select subfield-half adv-half jet-form-field-container\" data-field=\"build_type\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Type de projet<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><select  required=\"required\" name=\"build_type\" data-field-name=\"build_type\" id=\"build_type\" data-default-val=\"\" class=\"jet-form__field select-field\"><option value=\"module\" >Module<\/option><option value=\"kit\" >Kit \/ panneaux<\/option><\/select><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Modulaire ou en kit<\/span><\/small><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-6  field-type-select subfield-half adv-half jet-form-field-container\" data-field=\"foundation_type\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Type de fondation<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><select  required=\"required\" name=\"foundation_type\" data-field-name=\"foundation_type\" id=\"foundation_type\" class=\"jet-form__field select-field\"><option value=\"slab\" >Dalle de b\u00e9ton<\/option><option value=\"crawl\" >Vide sanitaire<\/option><option value=\"basement\" >Fondation (sous-sol)<\/option><option value=\"piers\" >Pieux<\/option><\/select><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Dalle de b\u00e9ton, vide sanitaire, sous-sol ou pieux<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-6  field-type-text subfield-half adv-half jet-form-field-container\" data-field=\"dims\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Dimensions (optionnel)<\/span>\n\t<\/div><input class=\"jet-form__field text-field \" name=\"dims\" id=\"dims\" type=\"text\" data-field-name=\"dims\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Indique les dimensions si connues (ex. : 36x42). Laisse vide si inconnu.<\/span><\/small><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-12  field-type-heading adv-toggle jet-form-field-container\" data-field=\"heading_copy\" data-conditional=\"false\"><div class=\"jet-form__heading\">\n\t<span class=\"jet-form__label-text\" ><strong>2) R\u00e9glages avanc\u00e9s<\/strong><\/span>\n\t<\/div><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-12  field-type-heading adv-item adv-full jet-form-field-container\" data-field=\"heading_copy_copy\" data-conditional=\"false\"><div class=\"jet-form__heading\">\n\t<span class=\"jet-form__label-text\" ><strong>&nbsp;&nbsp;2.1) Structure et raccordements <\/strong><\/span>\n\t<\/div><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-6  field-type-text adv-item adv-half jet-form-field-container\" data-field=\"foundation\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" > Excavation et fondation (% ou $\/pi\u00b2)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" required=\"required\" name=\"foundation\" id=\"foundation\" type=\"text\" data-field-name=\"foundation\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Comprend l\u2019excavation, les semelles, les murs de fondation et la dalle de b\u00e9ton.<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-6  field-type-text adv-item adv-half jet-form-field-container\" data-field=\"connections\" data-conditional=\"[{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;module&quot;,&quot;set_value&quot;:&quot;5-10&quot;},{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;kit&quot;,&quot;set_value&quot;:&quot;30-55&quot;}]\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" > Installation et raccordements (%)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" required=\"required\" name=\"connections\" id=\"connections\" type=\"text\" data-field-name=\"connections\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Comprend la main-d\u2019\u0153uvre, l\u2019\u00e9quipement n\u00e9cessaire au montage ainsi que les raccordements \u00e9lectriques, de plomberie et aux r\u00e9seaux principaux.<\/span><\/small><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-12  field-type-heading adv-item adv-full jet-form-field-container\" data-field=\"heading_copy_copy_copy\" data-conditional=\"false\"><div class=\"jet-form__heading\">\n\t<span class=\"jet-form__label-text\" ><strong>&nbsp;&nbsp;2.2)  Finitions int\u00e9rieures et ext\u00e9rieures<\/strong><\/span>\n\t<\/div><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-4  field-type-text adv-item adv-third jet-form-field-container\" data-field=\"finish_interior\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Finition int\u00e9rieure (%)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" value=\"14\u201320\" required=\"required\" name=\"finish_interior\" id=\"finish_interior\" type=\"text\" data-field-name=\"finish_interior\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Travaux int\u00e9rieurs : couvre-planchers, pl\u00e2tre, peinture, moulures, etc.<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-4  field-type-text adv-item adv-third jet-form-field-container\" data-field=\"finish_exterior\" data-conditional=\"[{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;module&quot;,&quot;set_value&quot;:&quot;0-5&quot;},{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;kit&quot;,&quot;set_value&quot;:&quot;10-20&quot;}]\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Finition ext\u00e9rieure (%)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" required=\"required\" name=\"finish_exterior\" id=\"finish_exterior\" type=\"text\" data-field-name=\"finish_exterior\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Rev\u00eatement, goutti\u00e8res, patio, escaliers et \u00e9l\u00e9ments ext\u00e9rieurs visibles.<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-4  field-type-text adv-item adv-third jet-form-field-container\" data-field=\"finish_basement\" data-conditional=\"[{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;module&quot;,&quot;set_value&quot;:&quot;10-20&quot;},{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;kit&quot;,&quot;set_value&quot;:&quot;15-30&quot;}]\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Finition du sous-sol (%)<\/span>\n\t<\/div><input class=\"jet-form__field text-field \" name=\"finish_basement\" id=\"finish_basement\" type=\"text\" data-field-name=\"finish_basement\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Comprend l\u2019isolation, le plancher, les divisions et l\u2019escalier.<\/span><\/small><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-12  field-type-heading adv-item adv-full jet-form-field-container\" data-field=\"heading_copy_copy_copy_copy\" data-conditional=\"false\"><div class=\"jet-form__heading\">\n\t<span class=\"jet-form__label-text\" ><strong>&nbsp;&nbsp;2.3)  Am\u00e9nagements ext\u00e9rieurs et \u00e9quipements<\/strong><\/span>\n\t<\/div><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-6  field-type-text adv-item adv-half jet-form-field-container\" data-field=\"landscaping\" data-conditional=\"[{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;module&quot;,&quot;set_value&quot;:&quot;5-10&quot;},{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;kit&quot;,&quot;set_value&quot;:&quot;10-20&quot;}]\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Paysagement et am\u00e9nagement ext\u00e9rieur (%)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" required=\"required\" name=\"landscaping\" id=\"landscaping\" type=\"text\" data-field-name=\"landscaping\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Englobe le nivellement du terrain, les all\u00e9es, le gazon, les plantations et le mobilier ext\u00e9rieur.<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-6  field-type-text adv-item adv-half jet-form-field-container\" data-field=\"heating\" data-conditional=\"false\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >M\u00e9canique du b\u00e2timent<\/span>\n\t<\/div><input class=\"jet-form__field text-field \" placeholder=\"Ex. 35000-40000\" name=\"heating\" id=\"heating\" type=\"text\" data-field-name=\"heating\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Regroupe les syst\u00e8mes int\u00e9rieurs de la maison : \u00e9lectricit\u00e9, plomberie, chauffage et ventilation (\u00e0 l\u2019int\u00e9rieur du b\u00e2timent, hors raccordements aux r\u00e9seaux).<\/span><\/small><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-12  field-type-heading adv-item adv-full jet-form-field-container\" data-field=\"heading\" data-conditional=\"false\"><div class=\"jet-form__heading\">\n\t<span class=\"jet-form__label-text\" ><strong>&nbsp;&nbsp;2.4) Permis et impr\u00e9vus<\/strong><\/span>\n\t<\/div><\/div><\/div><div  class=\"jet-form-row\"><div class=\"jet-form-col jet-form-col-6  field-type-text adv-item adv-half jet-form-field-container\" data-field=\"permits\" data-conditional=\"[{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;module&quot;,&quot;set_value&quot;:&quot;1-3&quot;},{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;kit&quot;,&quot;set_value&quot;:&quot;2-5&quot;}]\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" > Permis et frais professionnels (%)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" value=\"3-5\" required=\"required\" name=\"permits\" id=\"permits\" type=\"text\" data-field-name=\"permits\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">Honoraires d\u2019architecte, ing\u00e9nieur, arpenteur, ainsi que les permis municipaux requis.<\/span><\/small><\/div><div class=\"jet-form-col jet-form-col-6  field-type-text adv-item adv-half jet-form-field-container\" data-field=\"contingency\" data-conditional=\"[{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;module&quot;,&quot;set_value&quot;:&quot;15-20&quot;},{&quot;type&quot;:&quot;set_value&quot;,&quot;field&quot;:&quot;build_type&quot;,&quot;operator&quot;:&quot;equal&quot;,&quot;value&quot;:&quot;kit&quot;,&quot;set_value&quot;:&quot;20-30&quot;}]\"><div class=\"jet-form__label\">\n\t<span class=\"jet-form__label-text\" >Impr\u00e9vus (recommand\u00e9) (%)<span class=\"jet-form__required\">*<\/span><\/span>\n\t<\/div><input class=\"jet-form__field text-field \" value=\"5\u201310\" required=\"required\" name=\"contingency\" id=\"contingency\" type=\"text\" data-field-name=\"contingency\"><small class=\"jet-form__desc\"><span class=\"jet-form__desc-text\">R\u00e9serve pour couvrir les ajustements de prix, retards ou ajouts non planifi\u00e9s.<\/span><\/small><\/div><\/div><div  class=\"jet-form-row jet-form-row--submit\"><div class=\"jet-form-col jet-form-col-12  field-type-submit  jet-form-field-container\" data-field=\"Submit\" data-conditional=\"false\"><div class=\"jet-form__submit-wrap\">\n\t\t<button class=\"jet-form__submit submit-type-reload\" type=\"submit\">Calculer l\u2019estimation<\/button>\n<\/div><\/div><\/div><input class=\"jet-form__field hidden-field\" type=\"hidden\" name=\"_jet_engine_nonce\" value=\"fbcc80c8df\" data-field-name=\"_jet_engine_nonce\"><\/form>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-16d6a38 e-con-full e-flex e-con e-child\" data-id=\"16d6a38\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-69af5db elementor-widget elementor-widget-html\" data-id=\"69af5db\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"simu-notes\">\r\n  <h4>Notes importantes<\/h4>\r\n  <ul>\r\n    <li>Les pourcentages indiquent la <strong>r\u00e9partition estim\u00e9e des co\u00fbts<\/strong> pour un projet type au Qu\u00e9bec.<\/li>\r\n    <li>Les montants affich\u00e9s repr\u00e9sentent une <strong>fourchette indicative<\/strong> (min\u2013max) bas\u00e9e sur le march\u00e9 actuel de la construction pr\u00e9fabriqu\u00e9e.<\/li>\r\n    <li>Certains facteurs comme <strong>le terrain, les taxes, le zonage<\/strong> ou les conditions de sol peuvent influencer le co\u00fbt r\u00e9el du projet.<\/li>\r\n    <li>Les r\u00e9sultats ne remplacent pas une <strong>soumission officielle<\/strong> d\u2019un professionnel ou d\u2019un fabricant.<\/li>\r\n    <li><strong>Collection Immobili\u00e8re<\/strong> n\u2019assume aucune responsabilit\u00e9 quant \u00e0 l\u2019exactitude ou l\u2019utilisation des estimations fournies.<\/li>\r\n  <\/ul>\r\n<\/div>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-16ae742 simu-print e-flex e-con-boxed e-con e-parent\" data-id=\"16ae742\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-72ccf60 elementor-widget elementor-widget-html\" data-id=\"72ccf60\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- SIMU : R\u00e9sultats (+ bouton Exporter PDF) -->\r\n<style>\r\n  #simu-results{\r\n    --ci-border:#e5e7eb;\r\n    --ci-muted:#667085;\r\n    --ci-bg:#fff;\r\n    --ci-pill:#eef2ff;\r\n    --ci-ink:#0b1220;\r\n    --ci-blue:#0B5BAB;       \r\n    --ci-blue-dark:#094a99;\r\n  }\r\n  #simu-results *{box-sizing:border-box;}\r\n\r\n  \/* === Cartes et Grille === *\/\r\n  #simu-results .ci-card{\r\n    background:var(--ci-bg)!important;\r\n    border:1px solid var(--ci-border)!important;\r\n    border-radius:16px!important;\r\n    padding:16px!important;\r\n    box-shadow:0 1px 2px rgba(16,24,40,.06)!important;\r\n  }\r\n  #simu-results .ci-grid{display:grid!important;grid-template-columns:1fr!important;gap:16px!important;}\r\n  @media(min-width:900px){#simu-results .ci-grid{grid-template-columns:1.2fr .8fr!important;}}\r\n\r\n  \/* === Tableau === *\/\r\n  #simu-results .ci-table{width:100%!important;border-collapse:collapse!important;}\r\n  #simu-results .ci-table th,\r\n  #simu-results .ci-table td{\r\n    padding:12px 10px!important;\r\n    border-bottom:1px solid var(--ci-border)!important;\r\n    text-align:left!important;\r\n    font-size:14px!important;\r\n  }\r\n  #simu-results .ci-table th{\r\n    font-size:12px!important;\r\n    text-transform:uppercase!important;\r\n    letter-spacing:.04em!important;\r\n    color:var(--ci-muted)!important;\r\n  }\r\n  #simu-results .ci-right{text-align:right!important;}\r\n\r\n  \/* === Ligne Total Projet === *\/\r\n  #simu-results tfoot td.total{\r\n    font-size:22px!important;\r\n    font-weight:800!important;\r\n    color:var(--ci-ink)!important;\r\n  }\r\n\r\n  \/* === Co\u00fbt par pi\u00b2 (affichage page) === *\/\r\n  #simu-results .ci-psf{\r\n    margin-top:10px!important;\r\n    font-size:19px!important;\r\n    color:var(--ci-muted)!important;\r\n    font-style:italic!important;\r\n  }\r\n\r\n  \/* === R\u00e9sum\u00e9 === *\/\r\n  #simu-results h4{\r\n    font-size:16px!important;\r\n    font-weight:700!important;\r\n    color:var(--ci-ink)!important;\r\n    margin:0 0 10px!important;\r\n  }\r\n  #simu-results #simu-summary{\r\n    font-size:14px!important;\r\n    line-height:1.5!important;\r\n    color:var(--ci-ink)!important;\r\n  }\r\n  #simu-results #simu-summary .help{\r\n    font-size:13px!important;\r\n    color:var(--ci-muted)!important;\r\n  }\r\n  #simu-results #simu-summary .total{\r\n    display:block!important;\r\n    font-size:30px!important;\r\n    font-weight:900!important;\r\n    color:var(--ci-ink)!important;\r\n    line-height:1.2!important;\r\n    margin-top:6px!important;\r\n  }\r\n\r\n  \/* === Bouton === *\/\r\n  #simu-results .ci-actions{margin-top:14px;display:flex;justify-content:flex-start;}\r\n  #simu-results .ci-btn{\r\n    appearance:none;border:0;cursor:pointer;\r\n    background:var(--ci-blue);color:#fff;border-radius:12px;\r\n    padding:12px 18px;font-weight:800;letter-spacing:.02em;font-size:16px;\r\n    box-shadow:0 2px 0 rgba(0,0,0,.06), inset 0 -2px 0 rgba(0,0,0,.08);\r\n    transition:transform .05s ease, background .2s ease, box-shadow .2s ease;\r\n  }\r\n  #simu-results .ci-btn:hover{background:var(--ci-blue-dark);}\r\n  #simu-results .ci-btn:active{transform:translateY(1px);}\r\n  #simu-results .ci-btn svg{vertical-align:-2px;margin-right:8px}\r\n<\/style>\r\n\r\n<section id=\"simu-results\" class=\"ci-card\">\r\n  <h3>3) R\u00e9sultats<\/h3>\r\n  <div class=\"ci-grid\">\r\n    <!-- Tableau -->\r\n    <div class=\"ci-card\">\r\n      <table class=\"ci-table\" id=\"simu-table\">\r\n        <thead>\r\n          <tr>\r\n            <th>Poste<\/th>\r\n            <th>(min\u2013max)<\/th>\r\n            <th class=\"ci-right\">Montant min<\/th>\r\n            <th class=\"ci-right\">Montant max<\/th>\r\n          <\/tr>\r\n        <\/thead>\r\n        <tbody id=\"simu-rows\"><\/tbody>\r\n        <tfoot>\r\n          <tr>\r\n            <td class=\"total\" colspan=\"2\">Total projet<\/td>\r\n            <td id=\"simu-total-min\" class=\"ci-right total\">\u2014<\/td>\r\n            <td id=\"simu-total-max\" class=\"ci-right total\">\u2014<\/td>\r\n          <\/tr>\r\n        <\/tfoot>\r\n      <\/table>\r\n      <div id=\"simu-psf\" class=\"ci-psf\"><\/div>\r\n    <\/div>\r\n\r\n    <!-- R\u00e9sum\u00e9 -->\r\n    <div class=\"ci-card\">\r\n      <h4>R\u00e9sum\u00e9<\/h4>\r\n      <div id=\"simu-summary\" class=\"ci-muted\">\r\n        Remplis les champs puis clique \u00ab Calculer \u00bb.\r\n      <\/div>\r\n\r\n      <!-- Bouton Exporter PDF -->\r\n      <div class=\"ci-actions\">\r\n        <button type=\"button\" class=\"ci-btn\" id=\"ci-export-pdf\">\r\n          <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\r\n            <path d=\"M12 3v10m0 0l4-4m-4 4l-4-4M4 17v2a2 2 0 002 2h12a2 2 0 002-2v-2\"\r\n                  stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\r\n          <\/svg>\r\n          Exporter PDF\r\n        <\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/section>\r\n\r\n<!-- Script 1 : \u201cFourchette totale\u201d en gros -->\r\n<script>\r\n(function(){\r\n  const el = document.getElementById('simu-summary');\r\n  if(!el) return;\r\n  const upsize = () => {\r\n    if (el.querySelector('.total')) return;\r\n    const rx = \/(Fourchette totale\\s*:?\\s*)([\\d\\s\\u00A0.,\\$]+)\\s*[\\u2013\\-]\\s*([\\d\\s\\u00A0.,\\$]+)\/i;\r\n    const html = el.innerHTML;\r\n    if (rx.test(html)) {\r\n      el.innerHTML = html.replace(rx, (_, label, a, b) =>\r\n        `${label}<div class=\"total\">${a} \u2013 ${b}<\/div>`\r\n      );\r\n    }\r\n  };\r\n  upsize();\r\n  new MutationObserver(upsize).observe(el, {childList:true, subtree:true, characterData:true});\r\n})();\r\n<\/script>\r\n\r\n<!-- Script 2 : g\u00e9n\u00e9ration PDF + logo PNG -->\r\n<script>\r\n(function(){\r\n  const btn = document.getElementById('ci-export-pdf');\r\n  if(!btn) return;\r\n\r\n  const CI_BLUE = [11, 91, 171];\r\n  const BORDER  = [229, 231, 235];\r\n  const MUTED   = [102, 112, 133];\r\n\r\n  const sanitize = (s='') => s\r\n    .replace(\/\\u00A0\/g, ' ')\r\n    .replace(\/[\\u2013\\u2014]\/g, '-')\r\n    .replace(\/[\u00d7\u2715]\/g, 'x')\r\n    .replace(\/\\u00B2\/g, '2');\r\n  const getText = el => sanitize(el ? el.innerText : '');\r\n\r\n  btn.addEventListener('click', () => {\r\n    const table   = document.getElementById('simu-table');\r\n    const summary = document.getElementById('simu-summary');\r\n    const psf     = document.getElementById('simu-psf');\r\n    if(!table || !summary){\r\n      alert(\"Aucun r\u00e9sultat \u00e0 exporter. Calcule d\u2019abord l\u2019estimation.\");\r\n      return;\r\n    }\r\n\r\n    const { jsPDF } = window.jspdf || {};\r\n    if (!jsPDF) { alert(\"La librairie jsPDF n\u2019est pas encore charg\u00e9e.\"); return; }\r\n\r\n    const doc = new jsPDF({ unit: 'mm', format: 'a4', orientation: 'portrait' });\r\n    const pageW = doc.internal.pageSize.getWidth();\r\n    const pageH = doc.internal.pageSize.getHeight();\r\n    const marginX = 14;\r\n    let y = 14;\r\n\r\n    \/\/ === Logo PNG en haut \u00e0 droite ===\r\n    const logoUrl = \"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/logo2-800-x-400-px.png\";\r\n    const logoImg = new Image();\r\n    logoImg.crossOrigin = \"anonymous\";\r\n    logoImg.onload = function(){\r\n      const logoWmm = 32;  \/\/ largeur du logo en mm\r\n      const ratio = (logoImg.naturalHeight||300) \/ (logoImg.naturalWidth||800);\r\n      const logoHmm = Math.max(6, logoWmm * ratio);\r\n      const x = pageW - marginX - logoWmm;\r\n      const yLogo = 8;\r\n      doc.addImage(logoImg, \"PNG\", x, yLogo, logoWmm, logoHmm);\r\n\r\n      doExport();\r\n    };\r\n    logoImg.onerror = function(){\r\n      console.warn(\"Logo non charg\u00e9, export sans logo.\");\r\n      doExport();\r\n    };\r\n    logoImg.src = logoUrl;\r\n\r\n    function doExport(){\r\n      const title = \"Estimation de projet \u2014 R\u00e9sultats\";\r\n      const now   = new Date().toLocaleString('fr-CA');\r\n\r\n      doc.setFont('helvetica','bold'); doc.setFontSize(14); doc.setTextColor(0,0,0);\r\n      doc.text(title, marginX, y); y += 5.5;\r\n\r\n      doc.setFont('helvetica','normal'); doc.setFontSize(9.5); doc.setTextColor(...MUTED);\r\n      doc.text(`G\u00e9n\u00e9r\u00e9 le ${now}`, marginX, y); y += 3.5;\r\n\r\n      doc.setDrawColor(...BORDER); doc.setLineWidth(0.2);\r\n      doc.line(marginX, y, pageW - marginX, y); y += 6;\r\n\r\n      \/\/ Tableau\r\n      const headers = Array.from(table.querySelectorAll('thead th')).map(th => sanitize(th.innerText.trim()));\r\n      const bodyRows = Array.from(table.querySelectorAll('tbody tr')).map(tr =>\r\n        Array.from(tr.children).map(td => sanitize(td.innerText.trim()))\r\n      );\r\n      const tfootTr = table.querySelector('tfoot tr');\r\n      let totalRow = tfootTr ? Array.from(tfootTr.children).map(td => sanitize(td.innerText.trim())) : null;\r\n\r\n      const head = [headers];\r\n      const body = bodyRows.slice();\r\n      if (totalRow) body.push(totalRow);\r\n\r\n      doc.autoTable({\r\n        head, body,\r\n        startY: y,\r\n        margin: { left: marginX, right: marginX },\r\n        styles: { font:'helvetica', fontSize:9.2, cellPadding:2.2, lineColor:BORDER, lineWidth:0.2, textColor:[17,24,39] },\r\n        headStyles: { fillColor:[247,249,252], textColor:[71,85,105], fontStyle:'bold' },\r\n        alternateRowStyles: { fillColor:[252,253,255] },\r\n        columnStyles: { 2:{halign:'right'}, 3:{halign:'right'} },\r\n        didParseCell(data){\r\n          if (totalRow && data.section==='body' && data.row.index === body.length-1){\r\n            data.cell.styles.fontStyle = 'bold';\r\n            data.cell.styles.fillColor = [248,250,252];\r\n            data.cell.styles.lineWidth = 0.4;\r\n          }\r\n          const h = headers[data.column.index]?.toLowerCase() || '';\r\n          if (h.includes('(min') || h.includes('min\u2013max') || h.includes('min-max')){\r\n            data.cell.styles.halign = 'center';\r\n          }\r\n        },\r\n        didDrawPage(){\r\n          const n = doc.getNumberOfPages();\r\n          doc.setFont('helvetica','normal'); doc.setFontSize(8.5); doc.setTextColor(...MUTED);\r\n          doc.text(`Page ${n}`, pageW - marginX, pageH - 6, { align:'right' });\r\n          doc.setTextColor(0,0,0);\r\n        }\r\n      });\r\n\r\n      y = doc.lastAutoTable.finalY + 6;\r\n\r\n      \/\/ Co\u00fbt par pi2\r\n      const psfRaw = getText(psf);\r\n      if (psfRaw){\r\n        let t = psfRaw\r\n          .replace(\/[\u201c\u201d\u00ab\u00bb\"'\u201d]\/g, \"\")\r\n          .replace(\/[~\u2248]\/g, \"\")\r\n          .replace(\/\\s+\/g, \" \")\r\n          .trim();\r\n\r\n        const m = t.match(\/([\\d\\s.,]+)\\s*\\$\\s*(?:\u00e0|-)\\s*([\\d\\s.,]+)\\s*\\$\/i);\r\n        if (m){\r\n          const left  = m[1].replace(\/\\s+\/g,\" \").trim();\r\n          const right = m[2].replace(\/\\s+\/g,\" \").trim();\r\n          t = `${left} $ \u00e0 ${right} $ \/ pi2`;\r\n        } else {\r\n          t = t.replace(\/\\s*\\\/\\s*(pi.?2)?$\/i,\"\").trim() + \" \/ pi2\";\r\n        }\r\n\r\n        doc.setFont(\"helvetica\",\"italic\"); doc.setFontSize(10);\r\n        doc.setTextColor(...MUTED);\r\n        doc.text(t, marginX, y);\r\n        y += 8;\r\n        doc.setTextColor(0,0,0);\r\n        doc.setFont(\"helvetica\",\"normal\");\r\n      }\r\n\r\n      \/\/ Encadr\u00e9 R\u00e9sum\u00e9\r\n      const sumDiv = summary.cloneNode(true);\r\n      const totalEl = sumDiv.querySelector('.total');\r\n      if (totalEl) totalEl.textContent = sanitize(totalEl.textContent);\r\n\r\n      let sumText = getText(sumDiv).replace(\/\\r?\\n\\r?\\n\/g,\"\\n\").trim() || \"\u2014\";\r\n\r\n      doc.setFont('helvetica','bold'); doc.setFontSize(12);\r\n      const boxX = marginX, boxW = pageW - marginX*2, boxPad = 6;\r\n      const titleH = 6;\r\n\r\n      doc.setFont('helvetica','normal'); doc.setFontSize(10);\r\n      const lines = doc.splitTextToSize(sumText, boxW - boxPad*2);\r\n      const textHeight = lines.length * 4.2;\r\n      const boxH = titleH + 6 + textHeight + boxPad;\r\n\r\n      doc.setDrawColor(...CI_BLUE); doc.setLineWidth(0.5);\r\n      if (doc.roundedRect) doc.roundedRect(boxX, y, boxW, boxH, 2.5, 2.5, 'S');\r\n      else doc.rect(boxX, y, boxW, boxH, 'S');\r\n\r\n      doc.setFont('helvetica','bold'); doc.setFontSize(12); doc.setTextColor(0,0,0);\r\n      doc.text('R\u00e9sum\u00e9', boxX + boxPad, y + titleH);\r\n\r\n      doc.setFont('helvetica','normal'); doc.setFontSize(10); doc.setTextColor(0,0,0);\r\n      doc.text(lines, boxX + boxPad, y + titleH + 6);\r\n\r\n      doc.save(`estimation-${Date.now()}.pdf`);\r\n    }\r\n  });\r\n})();\r\n<\/script>\r\n\r\n<!-- Librairies PDF (CDN) -->\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.1\/jspdf.umd.min.js\" crossorigin=\"anonymous\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.8.2\/jspdf.plugin.autotable.min.js\" crossorigin=\"anonymous\"><\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-dd5df8b elementor-widget elementor-widget-text-editor\" data-id=\"dd5df8b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>\u00a9 Collection Immobili\u00e8re \u2014 Simulateur. Les r\u00e9sultats sont indicatifs et ne remplacent pas une soumission officielle.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d0035e4 e-flex e-con-boxed e-con e-parent\" data-id=\"d0035e4\" data-element_type=\"container\" data-e-type=\"container\" id=\"comparateur\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-d98fa73 elementor-widget elementor-widget-html\" data-id=\"d98fa73\" data-element_type=\"widget\" data-e-type=\"widget\" id=\"comparateur\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================\r\n     COMPARATEUR \u2014 Collection Immobili\u00e8re\r\n     (HTML + CSS + JS complet \u2022 export PDF vectoriel + LOGO 60px)\r\n     ========================= -->\r\n<section id=\"simu-compare\" class=\"ci-card\">\r\n  <header class=\"simu-compare__head\">\r\n    <h2 class=\"simu-compare__title\">Comparateur de co\u00fbts pr\u00e9fab<\/h2>\r\n    <p class=\"simu-compare__lead\">Sauvegardez jusqu\u2019\u00e0 <strong>3 estimations<\/strong> et comparez-les c\u00f4te \u00e0 c\u00f4te.<\/p>\r\n    <div class=\"simu-compare__actions\">\r\n      <button class=\"ci-btn\" id=\"sc-import-last\">Importer ma derni\u00e8re estimation<\/button>\r\n      <button class=\"ci-btn\" id=\"sc-export-pdf\">G\u00e9n\u00e9rer PDF<\/button>\r\n      <button class=\"ci-btn\" id=\"sc-clear\">Vider<\/button>\r\n    <\/div>\r\n  <\/header>\r\n\r\n  <div class=\"simu-compare__status\">\r\n    <span><strong id=\"sc-count\">0<\/strong>\/3 s\u00e9lection(s)<\/span>\r\n    <button class=\"ci-btn\" id=\"sc-open-modal\" disabled>Ouvrir le comparatif<\/button>\r\n  <\/div>\r\n\r\n  <!-- Aper\u00e7us -->\r\n  <div class=\"sc-cards\" id=\"sc-cards\"><\/div>\r\n\r\n  <!-- Modal comparatif -->\r\n  <dialog id=\"sc-modal\" class=\"sc-modal\">\r\n    <div class=\"sc-modal__box\">\r\n      <div class=\"sc-modal__head\">\r\n        <h3>Tableau comparatif des estimations<\/h3>\r\n        <button class=\"sc-close\" id=\"sc-close\" aria-label=\"Fermer\">\u00d7<\/button>\r\n      <\/div>\r\n      <div class=\"sc-modal__content\">\r\n        <div class=\"sc-table-wrap\" id=\"sc-table-wrap\"><\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/dialog>\r\n\r\n  <noscript><p>Activez JavaScript pour utiliser le comparateur.<\/p><\/noscript>\r\n<\/section>\r\n\r\n<style>\r\n  \/* \u2014\u2014\u2014 Variables & conteneur \u2014\u2014\u2014 *\/\r\n  #simu-compare{\r\n    --ci-border:#e5e7eb; --ci-muted:#667085; --ci-bg:#fff; --ci-ink:#0b1220;\r\n    --ci-blue:#0B5BAB; --ci-blue-dark:#094a99; --row-alt:#f8fafc;\r\n  }\r\n  #simu-compare *{box-sizing:border-box}\r\n  #simu-compare.ci-card{\r\n    background:var(--ci-bg)!important;border:1px solid var(--ci-border)!important;border-radius:16px!important;\r\n    padding:18px!important;box-shadow:0 1px 2px rgba(16,24,40,.06)!important;color:#334155!important\r\n  }\r\n  .simu-compare__head{display:grid;gap:12px;margin-bottom:10px}\r\n  .simu-compare__title{margin:0;color:var(--ci-ink)}\r\n  .simu-compare__lead{margin:0;color:var(--ci-ink)}\r\n  .simu-compare__actions{display:flex;gap:8px;flex-wrap:wrap}\r\n  .simu-compare__status{\r\n    display:flex;justify-content:space-between;align-items:center;margin:12px 0;padding:10px;\r\n    border:1px dashed var(--ci-border);border-radius:12px;background:#f9fbff\r\n  }\r\n\r\n  \/* \u2014\u2014\u2014 Boutons \u2014\u2014\u2014 *\/\r\n  #simu-compare .ci-btn{\r\n    appearance:none;border:0;cursor:pointer;background:var(--ci-blue);color:#fff;border-radius:12px;\r\n    padding:12px 18px;font-weight:800;letter-spacing:.02em;font-size:16px;\r\n    box-shadow:0 2px 0 rgba(0,0,0,.06), inset 0 -2px 0 rgba(0,0,0,.08);\r\n    transition:transform .05s ease, background .2s ease\r\n  }\r\n  #simu-compare .ci-btn:hover{background:var(--ci-blue-dark)}\r\n  #simu-compare .ci-btn:active{transform:translateY(1px)}\r\n  #simu-compare .ci-btn:disabled{opacity:.6;cursor:not-allowed}\r\n\r\n  \/* \u2014\u2014\u2014 Cartes \u2014\u2014\u2014 *\/\r\n  .sc-cards{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}\r\n  .sc-card{border:1px solid var(--ci-border);border-radius:12px;padding:12px;background:#fff;display:flex;flex-direction:column;gap:8px}\r\n  .sc-card__top{display:flex;justify-content:space-between;gap:10px}\r\n  .sc-card__label{font-weight:800;color:var(--ci-ink)}\r\n  .sc-badge{font-size:12px;border:1px solid var(--ci-border);background:#f5f7fb;border-radius:999px;padding:3px 8px;margin-right:6px}\r\n  .sc-close{appearance:none;background:transparent;border:none;font-size:22px;line-height:1;cursor:pointer}\r\n\r\n  \/* \u2014\u2014\u2014 Tableau \u2014\u2014\u2014 *\/\r\n  .sc-table{width:100%;border-collapse:separate;border-spacing:0;table-layout:auto}\r\n  .sc-table thead th{\r\n    position:sticky;top:0;background:#f6f8fb;z-index:1;text-transform:uppercase;letter-spacing:.04em;\r\n    color:var(--ci-muted);font-size:12px;padding:10px 12px;border-bottom:1px solid #e8edf4;text-align:right;white-space:nowrap\r\n  }\r\n  .sc-table th,.sc-table td{\r\n    padding:8px 12px;border-bottom:1px solid #e8edf4;font-size:14px;vertical-align:top;text-align:right;white-space:nowrap\r\n  }\r\n  .sc-colhead{\r\n    min-width:210px;color:var(--ci-ink);position:sticky;left:0;background:#eef3fb;border-right:1px solid #e6ecfb;text-align:left\r\n  }\r\n  .sc-table tbody tr:nth-child(even) td, .sc-table tbody tr:nth-child(even) th.sc-colhead{background:var(--row-alt)}\r\n  .num{font-variant-numeric:tabular-nums}\r\n  .sc-total{background:#f1f6ff!important}\r\n  .big{font-weight:800;color:var(--ci-ink);font-size:15px}\r\n  .sub{display:block;font-size:12px;color:#64748b;line-height:1.25;margin-top:2px;white-space:normal;max-width:22ch}\r\n\r\n  @media (max-width:900px){\r\n    .sc-cards{grid-template-columns:1fr}\r\n    .sc-colhead{position:static}\r\n    .sub{max-width:none}\r\n  }\r\n\r\n  \/* \u2014\u2014\u2014 Modal \u2014\u2014\u2014 *\/\r\n  .sc-modal{border:none;padding:0}\r\n  .sc-modal::backdrop{background:rgba(0,0,0,.4)}\r\n  .sc-modal__box{\r\n    max-width:1100px;width:96vw;border-radius:14px;background:#fff;border:1px solid var(--ci-border);\r\n    box-shadow:0 10px 30px rgba(9,14,27,.25)\r\n  }\r\n  .sc-modal__head{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--ci-border)}\r\n  .sc-modal__content{padding:14px 16px}\r\n\r\n  \/* \u2014\u2014\u2014 MOBILE ONLY \u2014\u2014\u2014 *\/\r\n  @media (max-width: 640px){\r\n    #simu-compare .simu-compare__status{ flex-direction: column; align-items: stretch; gap: 10px; padding: 12px; border-radius: 12px; background: #f2f6ff; }\r\n    #simu-compare .simu-compare__status > span{ order: 2; font-size: 14px; text-align: center; color: #334155; }\r\n    #simu-compare #sc-open-modal{ order: 1; width: 100%; padding: 14px 16px; font-size: 16px; border-radius: 10px; }\r\n    #simu-compare .simu-compare__actions{ flex-direction: column; }\r\n    #simu-compare .simu-compare__actions .ci-btn{ width: 100%; }\r\n    #simu-compare .sc-card{ padding: 10px; gap: 6px; }\r\n    #simu-compare .sc-badge{ font-size: 11px; padding: 2px 8px; }\r\n    #simu-compare .sc-table-wrap{ overflow-x: auto; -webkit-overflow-scrolling: touch; }\r\n    #simu-compare .sc-table th, #simu-compare .sc-table td{ padding: 8px 10px; font-size: 13px; white-space: nowrap; }\r\n    #simu-compare .sc-colhead{ position: static; min-width: 180px; }\r\n    #simu-compare .simu-compare__head{ gap: 8px; margin-bottom: 8px; }\r\n  }\r\n  @media (max-width: 640px){\r\n    #simu-compare .ci-btn{ font-size: 13.5px; padding: 8px 12px; border-radius: 8px; font-weight: 700; }\r\n    #simu-compare .simu-compare__actions{ flex-direction: column; gap: 5px; margin-top: 4px; }\r\n    #simu-compare #sc-open-modal{ width: 100%; padding: 10px 12px; font-size: 14px; font-weight: 700; border-radius: 8px; }\r\n    #simu-compare .simu-compare__status{ padding: 10px; gap: 6px; }\r\n    #simu-compare .simu-compare__status > span{ font-size: 13px; }\r\n  }\r\n<\/style>\r\n\r\n<script>\r\n(function(){\r\n  \/* =========================\r\n     Stockage & helpers\r\n     ========================= *\/\r\n  const STORE_KEY = 'ci_simu_compare_v1';\r\n  const el = s => document.querySelector(s);\r\n  const load = () => { try{ return JSON.parse(localStorage.getItem(STORE_KEY))||[] }catch(e){ return [] } };\r\n  const save = a => localStorage.setItem(STORE_KEY, JSON.stringify(a));\r\n  const uid  = () => 'SC-'+Math.random().toString(36).slice(2)+'-'+Date.now();\r\n  const fmt  = n => (Number(n||0)).toLocaleString('fr-CA');\r\n  const TYPE_MAP = { module:'Modulaire', kit:'Kit \/ panneaux' };\r\n\r\n  \/* =========================\r\n     PDF (pdfmake) + PATCH LOGO 60 px\r\n     ========================= *\/\r\n  const PDFMAKE_URL    = 'https:\/\/cdn.jsdelivr.net\/npm\/pdfmake@0.2.10\/build\/pdfmake.min.js';\r\n  const VFS_FONTS_URL  = 'https:\/\/cdn.jsdelivr.net\/npm\/pdfmake@0.2.10\/build\/vfs_fonts.js';\r\n\r\n  \/* 1) Constantes logo *\/\r\n  const LOGO_URL   = 'https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/logo2-800-x-400-px.png';\r\n  const LOGO_EMBED = ''; \/\/ optionnel: colle un dataURL ici pour fiabilit\u00e9 100%\r\n\r\n  \/* 2) Helpers pour r\u00e9cup\u00e9rer le logo en dataURL *\/\r\n  function imgToCanvasDataURL(url){\r\n    return new Promise((resolve) => {\r\n      try{\r\n        const img = new Image();\r\n        img.crossOrigin = 'anonymous';\r\n        img.src = url + (url.includes('?') ? '&' : '?') + 'v=' + Date.now();\r\n        img.onload = () => {\r\n          try{\r\n            const c = document.createElement('canvas');\r\n            c.width = img.naturalWidth || img.width;\r\n            c.height = img.naturalHeight || img.height;\r\n            const ctx = c.getContext('2d');\r\n            ctx.drawImage(img, 0, 0);\r\n            resolve(c.toDataURL('image\/png', 1.0));\r\n          }catch(e){ resolve(null); }\r\n        };\r\n        img.onerror = () => resolve(null);\r\n      }catch(e){ resolve(null); }\r\n    });\r\n  }\r\n  async function fetchToDataURL(url){\r\n    try{\r\n      const res = await fetch(url, {mode:'cors', credentials:'omit', cache:'no-cache', referrerPolicy:'no-referrer'});\r\n      const blob = await res.blob();\r\n      return await new Promise((resolve)=>{\r\n        const fr = new FileReader();\r\n        fr.onload = () => resolve(fr.result);\r\n        fr.readAsDataURL(blob);\r\n      });\r\n    }catch(e){ return null; }\r\n  }\r\n  async function getLogoData(){\r\n    if (LOGO_EMBED && LOGO_EMBED.startsWith('data:')) return LOGO_EMBED;\r\n    let data = await imgToCanvasDataURL(LOGO_URL);\r\n    if (data) return data;\r\n    data = await fetchToDataURL(LOGO_URL);\r\n    return data || null;\r\n  }\r\n\r\n  \/* 3) Chargement pdfmake *\/\r\n  function loadScript(src){\r\n    return new Promise((resolve, reject)=>{\r\n      if ([...document.scripts].some(s => s.src === src)) return resolve();\r\n      const s = document.createElement('script');\r\n      s.src = src; s.async = true;\r\n      s.onload = resolve;\r\n      s.onerror = ()=>reject(new Error('Fail '+src));\r\n      document.head.appendChild(s);\r\n    });\r\n  }\r\n  async function ensurePdfMake(){\r\n    if (!window.pdfMake) await loadScript(PDFMAKE_URL);\r\n    if (!window.pdfMake?.vfs) await loadScript(VFS_FONTS_URL);\r\n  }\r\n\r\n  \/* 4) Header pdfmake avec logo 60 px *\/\r\n  function headerWithLogo(logoDataUrl){\r\n    return {\r\n      margin:[30,24,30,10],\r\n      columns:[\r\n        [\r\n          { text: 'Comparateur d\u2019estimations', style:'h1', margin:[0,0,0,4] },\r\n          { text: `G\u00e9n\u00e9r\u00e9 le ${new Date().toLocaleString('fr-CA')}`, style:'meta' }\r\n        ],\r\n        (logoDataUrl\r\n          ? { image: logoDataUrl, width:60, alignment:'right' }  \/* 60 px *\/\r\n          : { text:'', width:60 })\r\n      ]\r\n    };\r\n  }\r\n\r\n  \/* =========================\r\n     Lecture du formulaire \/ normalisation\r\n     ========================= *\/\r\n  const escapeHTML = s => String(s||'').replace(\/[&<>\"]\/g,c=>({\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",\"\\\"\":\"&quot;\"}[c]));\r\n  const canonicalFoundation = s => {\r\n    s = String(s||'').trim();\r\n    if(!s) return '';\r\n    const low = s.normalize('NFD').replace(\/[\\u0300-\\u036f]\/g,'').toLowerCase();\r\n    if (\/dalle\/.test(low)) return 'Dalle de b\u00e9ton';\r\n    if (\/vide\\s*sanit\/.test(low)) return 'Vide sanitaire';\r\n    if (\/sous[-\\s]*sol|basement|fondation\/.test(low)) return 'Fondation (sous-sol)';\r\n    if (\/pieu\/.test(low)) return 'Pieux';\r\n    if (\/^slab$\/.test(low)) return 'Dalle de b\u00e9ton';\r\n    if (\/^crawl$\/.test(low)) return 'Vide sanitaire';\r\n    if (\/^basement$\/.test(low)) return 'Fondation (sous-sol)';\r\n    if (\/^piers?$\/.test(low)) return 'Pieux';\r\n    return s;\r\n  };\r\n  const cleanPercents = (s='') =>\r\n    String(s)\r\n      .replace(\/\\s*\u2022?\\s*\\d+\\s*[-\u2013]\\s*\\d+\\s*%\/gi,'')\r\n      .replace(\/\\s*\u2022?\\s*\\d+\\s*%\/gi,'')\r\n      .replace(\/\\(\\s*[+-]?\\d+\\s*%\\s*[^)]*\\)\/g,'')\r\n      .replace(\/\\s{2,}\/g,' ').trim();\r\n  const removeDollarPerSqft = (s='') =>\r\n    String(s).replace(\/\\$?\\s*\\\/\\s*(pi\\s*[\\u00B2^2]|pied[s]?\\s*carr[e\u00e9]s?)|(\\d+\\s*[-\u2013]\\s*\\d+\\s*\\$?\\s*\\\/\\s*pi[\\u00B2^2])\/ig,'').trim();\r\n  const qSel = (root, list) => list.map(sel => root.querySelector(sel)).find(Boolean);\r\n\r\n  const readFormType = ()=>{\r\n    const sel = qSel(document, ['#build_type','[name=\"build_type\"]','select[id*=\"build_type\"]','select[name*=\"build\"]']);\r\n    if(!sel) return null;\r\n    const val = (sel.value||'').toLowerCase().trim();\r\n    return TYPE_MAP[val] || sel.options?.[sel.selectedIndex]?.text || null;\r\n  };\r\n  const readFoundation = ()=>{\r\n    const candidates = [\r\n      document.getElementById('foundation_type'),\r\n      document.querySelector('select[name=\"foundation_type\"]'),\r\n      document.querySelector('select[id*=\"foundation_type\"]')\r\n    ];\r\n    for (const sel of candidates){\r\n      if (sel && sel.tagName === 'SELECT'){\r\n        const checked = sel.querySelector('option:checked');\r\n        const opt = checked || (sel.selectedIndex >= 0 ? sel.options[sel.selectedIndex] : null) || sel.options?.[0] || null;\r\n        const raw = (sel.value && sel.value.trim()) || opt?.text || opt?.value || '';\r\n        const label = canonicalFoundation(raw);\r\n        if (label) return label;\r\n      }\r\n    }\r\n    const any = Array.from(document.querySelectorAll('select')).find(s => \/foundation\/i.test((s.name||'')+(s.id||'')));\r\n    if (any){\r\n      const checked = any.querySelector('option:checked');\r\n      const opt = checked || (any.selectedIndex>=0 ? any.options[any.selectedIndex] : null) || any.options?.[0] || null;\r\n      const raw = (any.value && any.value.trim()) || opt?.text || opt?.value || '';\r\n      const label = canonicalFoundation(raw);\r\n      if (label) return label;\r\n    }\r\n    return '';\r\n  };\r\n  const readRegion = ()=>{\r\n    const sel = qSel(document, ['[name=\"region_factor\"]','[name*=\"region\"]','select[id*=\"region\"]']);\r\n    if(!sel) return null;\r\n    const txt = sel.options?.[sel.selectedIndex]?.text || sel.value || '';\r\n    return cleanPercents(txt);\r\n  };\r\n  const readDims = ()=>{\r\n    const input = qSel(document, ['#dims','[name=\"dims\"]','input[id*=\"dims\"]','[name*=\"dimension\"]']);\r\n    const v = (input?.value||'').trim();\r\n    return v || null;\r\n  };\r\n  const guessType = (label='')=>{\r\n    const s=(label||'').toLowerCase();\r\n    if(\/modul\/i.test(s)) return 'Modulaire';\r\n    if(\/(^|\\W)kit(\\W|$)|panneau|panel\/i.test(s)) return 'Kit \/ panneaux';\r\n    return '\u2014';\r\n  };\r\n\r\n  \/* =========================\r\n     API publique + logique cartes\/table\r\n     ========================= *\/\r\n  window.SimuCompare = {\r\n    add:r=>addScenario(r),\r\n    list:()=>load(),\r\n    clear:()=>{ save([]); render(); },\r\n    remove:id=>{ save(load().filter(x=>x.id!==id)); render(); }\r\n  };\r\n\r\n  function normalizeResult(r){\r\n    if(!r || typeof r!=='object') return null;\r\n    const id = r.id || uid();\r\n\r\n    const type = cleanPercents(removeDollarPerSqft((r.type && String(r.type).trim()) || readFormType() || guessType(r.label)));\r\n    let foundation = cleanPercents(removeDollarPerSqft(r.foundation || readFoundation() || ''));\r\n    const region = cleanPercents(removeDollarPerSqft(r.region || readRegion() || ''));\r\n    const dims = r.dims || readDims() || '';\r\n\r\n    if (!foundation) {\r\n      const late = readFoundation();\r\n      if (late) foundation = late;\r\n    }\r\n\r\n    const tot  = r.totals || { min:0, max:0 };\r\n    return {\r\n      id,\r\n      label: cleanPercents(removeDollarPerSqft(r.label || '')),\r\n      area: r.area ?? null,\r\n      type, foundation, region, dims,\r\n      totals: { min:+tot.min||0, max:+tot.max||0 },\r\n      breakdown: r.breakdown || {}\r\n    };\r\n  }\r\n\r\n  function addScenario(result){\r\n    const norm = normalizeResult(result || window.SIMU_LAST_RESULT);\r\n    if(!norm){ alert('Aucune estimation trouv\u00e9e \u00e0 ajouter.'); return; }\r\n    const arr = load();\r\n    if(arr.length>=3){ alert('Maximum 3 estimations.'); return; }\r\n    if(arr.some(x=>x.id===norm.id)) norm.id = uid();\r\n    arr.push(norm); save(arr); render();\r\n  }\r\n\r\n  function tr(label, cols, isTotal=false){\r\n    const cls = isTotal ? ' sc-total' : '';\r\n    const tds = cols.map(c=>`<td class=\"num\">${c}<\/td>`).join('');\r\n    return `<tr class=\"${cls}\"><th class=\"sc-colhead\">${escapeHTML(label)}<\/th>${tds}<\/tr>`;\r\n  }\r\n\r\n  function tableTemplate(arr){\r\n    if(arr.length<2) return '<p>Ajoutez au moins deux estimations pour afficher le tableau.<\/p>';\r\n\r\n    const mids    = arr.map(r => ((r.totals.min||0)+(r.totals.max||0))\/2 );\r\n    const baseMid = Math.min(...mids);\r\n    const deltas  = mids.map(m => ((m-baseMid)\/Math.max(baseMid,1))*100);\r\n    const spreads = arr.map(r => {\r\n      const min=r.totals.min||0, max=r.totals.max||0, mid=(min+max)\/2||1;\r\n      return Math.round(((max-min)\/mid)*100);\r\n    });\r\n\r\n    let html = '<table class=\"sc-table\"><thead><tr><th class=\"sc-colhead\">Poste \/ attribut<\/th>';\r\n    arr.forEach((r,i)=>{\r\n      const parts = [\r\n        r.area? (fmt(r.area)+' pi\u00b2') : null,\r\n        r.type || null,\r\n        r.foundation || null,\r\n        r.dims ? ('Dim. '+String(r.dims)) : null,\r\n        r.region || null\r\n      ].filter(Boolean);\r\n      const sub = parts.join(' \u2022 ');\r\n      html += `<th>Simulation ${i+1}<span class=\"sub\">${escapeHTML(sub||'')}<\/span><\/th>`;\r\n    });\r\n    html += '<\/tr><\/thead><tbody>';\r\n\r\n    html += tr('Total estim\u00e9', arr.map((r,i)=>{\r\n      const totalTxt = `<span class=\"big\">${fmt(r.totals.min)} \u2013 ${fmt(r.totals.max)} $<\/span>`;\r\n      const delta    = Math.round(deltas[i]);\r\n      const deltaTxt = (delta===0) ? 'diff\u00e9rence vs la moins ch\u00e8re : 0 % (r\u00e9f.)' : `diff\u00e9rence vs la moins ch\u00e8re : ${delta>0?'+':''}${delta} %`;\r\n      return `${totalTxt}<span class=\"sub\">\u2248 m\u00e9diane ${fmt(mids[i])} $ \u2022 \u00e9cart min\u2013max ${spreads[i]} % \u2022 ${deltaTxt}<\/span>`;\r\n    }), true);\r\n\r\n    const partsSet = new Set(); arr.forEach(r=>Object.keys(r.breakdown||{}).forEach(k=>partsSet.add(k)));\r\n    Array.from(partsSet).forEach(p=>{\r\n      html += tr(p, arr.map(r=>{\r\n        const o=(r.breakdown||{})[p]||{min:0,max:0};\r\n        return `<span class=\"num\">${fmt(o.min)} \u2013 ${fmt(o.max)} $<\/span>`;\r\n      }));\r\n    });\r\n\r\n    html += '<\/tbody><\/table>';\r\n    return html;\r\n  }\r\n\r\n  function cardTemplate(r){\r\n    const title = r.area ? `${fmt(r.area)} pi\u00b2` : 'Estimation';\r\n    const chips = [\r\n      r.type || '\u2014',\r\n      r.foundation || '\u2014',\r\n      r.area ? (fmt(r.area)+' pi\u00b2') : null\r\n    ].filter(Boolean).map(x=>`<span class=\"sc-badge\">${escapeHTML(x)}<\/span>`).join('');\r\n    return `<article class=\"sc-card\" data-id=\"${r.id}\">\r\n      <div class=\"sc-card__top\">\r\n        <div>\r\n          <div class=\"sc-card__label\">${escapeHTML(title)}<\/div>\r\n          <div class=\"sc-meta big\">${fmt(r.totals.min)} \u2013 ${fmt(r.totals.max)} $<\/div>\r\n        <\/div>\r\n        <button class=\"sc-close\" data-simu-remove=\"${r.id}\" title=\"Retirer\">\u00d7<\/button>\r\n      <\/div>\r\n      <div>${chips}<\/div>\r\n    <\/article>`;\r\n  }\r\n\r\n  function render(){\r\n    const arr = load();\r\n    el('#sc-count').textContent = arr.length;\r\n    el('#sc-open-modal').disabled = arr.length<2;\r\n    el('#sc-cards').innerHTML = arr.map(cardTemplate).join('') || `<div class=\"sc-card\"><div class=\"sc-card__top\"><span class=\"sc-card__label\">Aucune s\u00e9lection<\/span><\/div><div>Ajoutez vos estimations via \u00ab Importer ma derni\u00e8re estimation \u00bb.<\/div><\/div>`;\r\n    el('#sc-table-wrap').innerHTML = tableTemplate(arr);\r\n  }\r\n\r\n  \/* =========================\r\n     DocDefinition (pdfmake) \u2022 design propre\r\n     ========================= *\/\r\n  function buildDocDefinition(arr, logoDataUrl){\r\n    const INK='#0b1220', MUTED='#667085', BORDER='#e5e7eb';\r\n    const HEAD_BG='#f6f8fb', COL_BG='#eef3fb', TOTAL_BG='#f1f6ff', ZEBRA='#f8fafc';\r\n\r\n    const mids   = arr.map(r => ((r.totals.min||0)+(r.totals.max||0))\/2 );\r\n    const base   = Math.min(...mids);\r\n    const deltaP = mids.map(m => Math.round(((m-base)\/Math.max(base,1))*100));\r\n    const spread = arr.map(r => {\r\n      const min=r.totals.min||0, max=r.totals.max||0, mid=(min+max)\/2||1;\r\n      return Math.round(((max-min)\/mid)*100);\r\n    });\r\n\r\n    const simHeaders = arr.map((r,i)=>{\r\n      const parts = [\r\n        r.area? (fmt(r.area)+' pi\u00b2') : null,\r\n        r.type || null,\r\n        r.foundation || null,\r\n        r.dims ? ('Dim. '+String(r.dims)) : null,\r\n        r.region || null\r\n      ].filter(Boolean).join(' \u2022 ');\r\n      return {\r\n        stack:[\r\n          { text: 'SIMULATION '+(i+1), style: 'th', alignment:'right', margin:[0,0,0,2] },\r\n          { text: parts, style: 'sub', alignment:'right' }\r\n        ]\r\n      };\r\n    });\r\n\r\n    const totalRow = [\r\n      { text:'TOTAL ESTIM\u00c9', style:'rowHead' },\r\n      ...arr.map((r,i)=>{\r\n        const deltaTxt = (deltaP[i]===0) ? 'diff\u00e9rence vs la moins ch\u00e8re : 0 % (r\u00e9f.)'\r\n          : `diff\u00e9rence vs la moins ch\u00e8re : ${deltaP[i]>0?'+':''}${deltaP[i]} %`;\r\n        return {\r\n          fillColor: TOTAL_BG,\r\n          stack:[\r\n            { text:`${fmt(r.totals.min)} \u2013 ${fmt(r.totals.max)} $`, style:'total' , alignment:'right', margin:[0,2,0,0] },\r\n            { text:`\u2248 m\u00e9diane ${fmt(mids[i])} $ \u2022 \u00e9cart min\u2013max ${spread[i]} % \u2022 ${deltaTxt}`, style:'sub', alignment:'right' }\r\n          ]\r\n        };\r\n      })\r\n    ];\r\n\r\n    const partsSet = new Set(); arr.forEach(r=>Object.keys(r.breakdown||{}).forEach(k=>partsSet.add(k)));\r\n    const bodyRows = Array.from(partsSet).map((label)=>{\r\n      const row = [{ text: String(label).toUpperCase(), style:'rowHead', fillColor: COL_BG }];\r\n      arr.forEach(r=>{\r\n        const o=(r.breakdown||{})[label]||{min:0,max:0};\r\n        row.push({ text:`${fmt(o.min)} \u2013 ${fmt(o.max)} $`, alignment:'right' });\r\n      });\r\n      return row;\r\n    });\r\n\r\n    const headerRow = [\r\n      { text:'POSTE \/ ATTRIBUT', style:'th', alignment:'left' },\r\n      ...simHeaders\r\n    ];\r\n    const widths = ['*', ...arr.map(()=> 'auto')];\r\n\r\n    return {\r\n      pageSize:'A4',\r\n      pageMargins:[30,82,30,40],\r\n      defaultStyle:{ font:'Roboto', fontSize:10, color:INK },\r\n      styles:{\r\n        h1:{ fontSize:18, bold:true, color:INK },\r\n        meta:{ fontSize:9, color:MUTED },\r\n        th:{ fontSize:9, color:MUTED, bold:true, fillColor:HEAD_BG, margin:[6,6,6,6] },\r\n        sub:{ fontSize:9, color:'#64748b' },\r\n        rowHead:{ bold:true, color:INK, margin:[6,6,6,6] },\r\n        total:{ bold:true, fontSize:12, color:INK },\r\n        footer:{ fontSize:8, color:MUTED }\r\n      },\r\n      header: ()=> headerWithLogo(logoDataUrl),\r\n      footer: (currentPage, pageCount)=>({\r\n        margin:[30,8,30,14],\r\n        columns:[\r\n          { text:'collectionimmobiliere.com', style:'footer' },\r\n          { text:`\u00a9 Collection Immobili\u00e8re \u2022 Page ${currentPage}\/${pageCount}`, style:'footer', alignment:'right' }\r\n        ]\r\n      }),\r\n      content:[\r\n        {\r\n          layout: {\r\n            paddingLeft:  ()=> 6,\r\n            paddingRight: ()=> 6,\r\n            paddingTop:   ()=> 6,\r\n            paddingBottom:()=> 6,\r\n            hLineWidth: ()=> 0.75,\r\n            vLineWidth: ()=> 0.75,\r\n            hLineColor: BORDER,\r\n            vLineColor: BORDER,\r\n            fillColor: function (rowIndex){\r\n              if (rowIndex === 0) return null;\r\n              if (rowIndex === 1) return TOTAL_BG;\r\n              return (rowIndex % 2 === 0) ? ZEBRA : null;\r\n            }\r\n          },\r\n          table:{\r\n            widths,\r\n            headerRows:1,\r\n            body:[\r\n              headerRow,\r\n              totalRow,\r\n              ...bodyRows\r\n            ]\r\n          }\r\n        }\r\n      ]\r\n    };\r\n  }\r\n\r\n  \/* =========================\r\n     Export PDF (mobile-friendly)\r\n     ========================= *\/\r\n  async function exportPDF_vector(){\r\n    const arr = load();\r\n    if(arr.length<1){ alert('Aucune estimation \u00e0 exporter.'); return; }\r\n\r\n    await ensurePdfMake();\r\n\r\n    \/\/ R\u00e9cup\u00e9rer le logo (dataURL). Si LOGO_EMBED est vide, on tente URL -> canvas -> fetch\r\n    const logoDataUrl = await getLogoData();\r\n\r\n    const docDefinition = buildDocDefinition(arr, logoDataUrl);\r\n    const fname = `comparateur-prefab-${new Date().toISOString().slice(0,10)}.pdf`;\r\n\r\n    try{\r\n      window.pdfMake.createPdf(docDefinition).download(fname);\r\n    }catch(e){\r\n      window.pdfMake.createPdf(docDefinition).open();\r\n    }\r\n  }\r\n\r\n  \/* =========================\r\n     \u00c9v\u00e9nements\r\n     ========================= *\/\r\n  document.addEventListener('click', (ev)=>{\r\n    const t = ev.target;\r\n    if(t && t.closest('[data-simu-add-compare]')){ ev.preventDefault(); if (typeof window.SIMU_AFTER_CALC === 'function') window.SIMU_AFTER_CALC(); addScenario(window.SIMU_LAST_RESULT); }\r\n    if(t && t.id==='sc-import-last'){ ev.preventDefault(); if (typeof window.SIMU_AFTER_CALC === 'function') window.SIMU_AFTER_CALC(); addScenario(window.SIMU_LAST_RESULT); }\r\n    if(t && t.id==='sc-export-pdf'){ ev.preventDefault(); exportPDF_vector(); }\r\n    if(t && t.id==='sc-clear'){ ev.preventDefault(); window.SimuCompare.clear(); }\r\n    if(t && (t.id==='sc-open-modal' || t.closest?.('[data-simu-open]'))){ ev.preventDefault(); el('#sc-modal').showModal(); }\r\n    if(t && (t.id==='sc-close' || t.closest?.('#sc-close'))){ el('#sc-modal').close(); }\r\n    if(t && t.hasAttribute('data-simu-remove')){ const id = t.getAttribute('data-simu-remove'); window.SimuCompare.remove(id); }\r\n  }, {passive:false});\r\n\r\n  \/\/ Premier rendu\r\n  render();\r\n})();\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-707a146 e-flex e-con-boxed e-con e-parent\" data-id=\"707a146\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-83cf6d4 elementor-widget elementor-widget-html\" data-id=\"83cf6d4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<script>\r\n(function(){\r\n  \/* ===== Formats & parsers ===== *\/\r\n  const money = n => new Intl.NumberFormat('fr-CA',{style:'currency',currency:'CAD',maximumFractionDigits:0}).format(n||0);\r\n  const toNum = v => parseFloat(String(v||'').replace(\/\\s\/g,'').replace(',', '.')) || 0;\r\n\r\n  function parsePctRange(txt){\r\n    const s = String(txt||'').replace(',', '.').trim();\r\n    const m = s.match(\/([\\d.]+)\\s*%?\\s*[\u2013-]\\s*([\\d.]+)\\s*%?\/);\r\n    return m ? {min:(+m[1])\/100, max:(+m[2])\/100} : null;\r\n  }\r\n  function parsePsfRange(txt){\r\n    const s = String(txt||'').replace(',', '.').trim();\r\n    const m = s.match(\/([\\d.]+)\\s*[\u2013-]\\s*([\\d.]+)\/);\r\n    return m ? {min:+m[1], max:+m[2]} : null;\r\n  }\r\n  function footprintFromDims(v){\r\n    const m = String(v||'').match(\/(\\d+(?:\\.\\d+)?)\\s*[xX*]\\s*(\\d+(?:\\.\\d+)?)\/);\r\n    return m ? (+m[1])*(+m[2]) : 0;\r\n  }\r\n\r\n  \/* ===== Postes exprim\u00e9s en % du prix du module (hors fondation & m\u00e9canique) ===== *\/\r\n  const PARTS = [\r\n    ['foundation','Excavation et fondation'],          \/\/ trait\u00e9 \u00e0 part\r\n    ['connections','Installation et raccordements'],\r\n    ['finish_interior','Finition int\u00e9rieure'],\r\n    ['finish_exterior','Finition ext\u00e9rieure'],\r\n    ['finish_basement','Finition du sous-sol'],\r\n    ['landscaping','Paysagement et am\u00e9nagement ext\u00e9rieur'],\r\n    \/\/ ['heating','M\u00e9canique du b\u00e2timent'],            \/\/ g\u00e9r\u00e9 \u00e0 part en $ (ou % si saisi)\r\n    ['permits','Permis et frais professionnels'],\r\n    ['contingency','Impr\u00e9vus (recommand\u00e9)'],\r\n  ];\r\n\r\n  \/* ===== Helpers DOM \/ JFB ===== *\/\r\n  function waitForm(cb){\r\n    let tries=0;\r\n    const t=setInterval(()=>{\r\n      const f=document.querySelector('form.jet-form-builder, .jet-form-builder form, form[id*=\"jet-form\"], form[action*=\"jet-form\"], form[data-form-id]');\r\n      if(f||tries>80){clearInterval(t);cb(f||null);} tries++;\r\n    },200);\r\n  }\r\n  function findField(form,slug){\r\n    const sels=[\r\n      `[name=\"form_fields[${slug}]\"]`,\r\n      `input[name=\"form_fields[${slug}]\"]`,\r\n      `select[name=\"form_fields[${slug}]\"]`,\r\n      `[name$=\"[${slug}]\"]`,\r\n      `[name*=\"[${slug}]\"]`,\r\n      `[data-field-name=\"${slug}\"]`,\r\n      `[name=\"${slug}\"]`\r\n    ];\r\n    for(const s of sels){const el=form.querySelector(s)||document.querySelector(s); if(el) return el;}\r\n    return null;\r\n  }\r\n\r\n  \/* ===== M\u00c9CANIQUE DU B\u00c2TIMENT =====\r\n     - Priorit\u00e9 \u00e0 l\u2019entr\u00e9e manuelle (plage $ ou % dans le champ \"heating\")\r\n     - Sinon: auto par prix du module (+ l\u00e9ger scaling par superficie)\r\n     - Affichage dans le tableau: % uniquement\r\n  *\/\r\n  function readHeatingField(form){\r\n    return (\r\n      form?.querySelector('[name=\"form_fields[heating]\"]') ||\r\n      form?.querySelector('input[name=\"heating\"]') ||\r\n      document.querySelector('[name=\"form_fields[heating]\"], input[name=\"heating\"]')\r\n    );\r\n  }\r\n  function parseMoneyRange(txt){\r\n    if(!txt) return null;\r\n    const s = String(txt).replace(\/\\s\/g,'').replace(',', '.');\r\n    const m = s.match(\/([\\d.]+)\\s*[\u2013-]\\s*([\\d.]+)\/);\r\n    if(!m) return null;\r\n    const a=+m[1], b=+m[2];\r\n    if(!isFinite(a)||!isFinite(b)) return null;\r\n    return {min: Math.min(a,b), max: Math.max(a,b)};\r\n  }\r\n  function computeMechanicalAutoByPrice(modulePrice, areaPi2){\r\n    \/\/ paliers (ajuste au besoin)\r\n    let rng;\r\n    if (modulePrice < 175000)       rng = { min: 28000, max: 38000 };\r\n    else if (modulePrice < 275000)  rng = { min: 32000, max: 45000 };\r\n    else if (modulePrice < 400000)  rng = { min: 40000, max: 55000 };\r\n    else if (modulePrice < 600000)  rng = { min: 48000, max: 65000 };\r\n    else if (modulePrice < 900000)  rng = { min: 55000, max: 75000 };\r\n    else                            rng = { min: 65000, max: 85000 };\r\n\r\n    \/\/ micro-scaling superficie\r\n    let f = 1;\r\n    if (areaPi2 >= 1800 && areaPi2 < 2600) f = 1.08;\r\n    else if (areaPi2 >= 2600)              f = 1.15;\r\n    else if (areaPi2 > 0 && areaPi2 < 900) f = 0.92;\r\n\r\n    return { min: Math.round(rng.min * f), max: Math.round(rng.max * f) };\r\n  }\r\n\r\n  \/* ===== Attach \/ Compute ===== *\/\r\n  function attach(form){\r\n    if(!form) return;\r\n\r\n    const compute=(e)=>{\r\n      e?.preventDefault();\r\n\r\n      const area    = toNum(findField(form,'area')?.value);\r\n      const price   = toNum(findField(form,'module_price')?.value);\r\n      const region  = toNum(findField(form,'region_factor')?.value)||1;\r\n      const dimsVal = findField(form,'dims')?.value || '';\r\n      const fndVal  = findField(form,'foundation')?.value || '';\r\n      const fndType = (findField(form,'foundation_type')?.value || '').toLowerCase(); \/\/ slab | crawl | basement | piers\r\n\r\n      if(!area || !price){\r\n        alert('Veuillez entrer une superficie et un prix de module valides.');\r\n        return;\r\n      }\r\n\r\n      const footprint = footprintFromDims(dimsVal);\r\n      const hasDims   = !!footprint;\r\n\r\n      let sMin=0, sMax=0;         \/\/ cumul % (hors fondation & m\u00e9canique)\r\n      const rows = [];\r\n      const base = price * region;\r\n\r\n      let foundationRow = null;\r\n      let foundationAddMin = 0;\r\n      let foundationAddMax = 0;\r\n\r\n      \/* --- Fondation --- *\/\r\n      if(hasDims){\r\n        const rPsf = parsePsfRange(fndVal);\r\n        if(rPsf){\r\n          const minAmt = footprint * rPsf.min * region;\r\n          const maxAmt = footprint * rPsf.max * region;\r\n          foundationAddMin = minAmt;\r\n          foundationAddMax = maxAmt;\r\n          foundationRow = { label:'Excavation et fondation', txt:`${rPsf.min}\u2013${rPsf.max} $\/pi\u00b2`, minAmt, maxAmt };\r\n        }\r\n      } else {\r\n        const rPct = parsePctRange(fndVal);\r\n        if(rPct){\r\n          const minAmtPct = rPct.min * base;\r\n          const maxAmtPct = rPct.max * base;\r\n          const floorMin = 18000 * region;\r\n          const floorMax = 35000 * region;\r\n          const isPiers  = (fndType === 'piers');\r\n\r\n          if(!isPiers && maxAmtPct < floorMin){\r\n            foundationAddMin = floorMin;\r\n            foundationAddMax = floorMax;\r\n            foundationRow = { label:'Excavation et fondation', txt:'min 18 000\u201335 000 $', minAmt: floorMin, maxAmt: floorMax };\r\n          } else {\r\n            foundationRow = {\r\n              label:'Excavation et fondation',\r\n              txt:`${(rPct.min*100).toFixed(1)}\u2013${(rPct.max*100).toFixed(1)}%`,\r\n              minAmt: minAmtPct, maxAmt: maxAmtPct\r\n            };\r\n            sMin += rPct.min; sMax += rPct.max;\r\n          }\r\n        }\r\n      }\r\n\r\n      \/* --- Autres postes en % (sans m\u00e9canique) --- *\/\r\n      PARTS.forEach(([slug,label])=>{\r\n        if(slug==='foundation') return;\r\n        const r = parsePctRange(findField(form,slug)?.value);\r\n        if(r){\r\n          sMin += r.min; sMax += r.max;\r\n          rows.push({\r\n            label,\r\n            txt:`${(r.min*100).toFixed(1)}\u2013${(r.max*100).toFixed(1)}%`,\r\n            minAmt: r.min * base,\r\n            maxAmt: r.max * base\r\n          });\r\n        }\r\n      });\r\n\r\n      \/* --- M\u00e9canique du b\u00e2timent --- *\/\r\n      const heatingEl   = readHeatingField(form);\r\n      const manualMoney = heatingEl ? parseMoneyRange(heatingEl.value) : null;\r\n      const manualPct   = heatingEl ? parsePctRange(heatingEl.value)   : null;\r\n\r\n      let mechMin, mechMax, mechTxt;\r\n\r\n      if (manualMoney){\r\n        \/\/ entr\u00e9e manuelle en $\r\n        mechMin = manualMoney.min * region;\r\n        mechMax = manualMoney.max * region;\r\n        const pMin = Math.round((mechMin\/base)*1000)\/10;\r\n        const pMax = Math.round((mechMax\/base)*1000)\/10;\r\n        mechTxt = `${pMin}\u2013${pMax}%`;          \/\/ on n\u2019affiche que le %\r\n      } else if (manualPct){\r\n        \/\/ entr\u00e9e manuelle en %\r\n        mechMin = manualPct.min * base;\r\n        mechMax = manualPct.max * base;\r\n        mechTxt = `${(manualPct.min*100).toFixed(1)}\u2013${(manualPct.max*100).toFixed(1)}%`;\r\n      } else {\r\n        \/\/ auto par prix du module + superficie\r\n        const auto = computeMechanicalAutoByPrice(price, area);\r\n        mechMin = auto.min * region;\r\n        mechMax = auto.max * region;\r\n        const pMin = Math.round((mechMin\/base)*1000)\/10;\r\n        const pMax = Math.round((mechMax\/base)*1000)\/10;\r\n        mechTxt = `${pMin}\u2013${pMax}%`;\r\n      }\r\n\r\n      rows.push({ label:'M\u00e9canique du b\u00e2timent', txt: mechTxt, minAmt: mechMin, maxAmt: mechMax });\r\n      \/\/ (ne pas toucher \u00e0 sMin\/sMax : c\u2019est un poste en $)\r\n\r\n      \/* --- Totaux --- *\/\r\n      const totalMin = price * (1 + sMin) * region + foundationAddMin + mechMin;\r\n      const totalMax = price * (1 + sMax) * region + foundationAddMax + mechMax;\r\n\r\n      \/* --- Rendu tableau --- *\/\r\n      const tbody = document.getElementById('simu-rows');\r\n      if (tbody){\r\n        tbody.innerHTML = '';\r\n        const addRow=(l,txt,mi,ma)=>{\r\n          const tr=document.createElement('tr');\r\n          tr.innerHTML = `<td>${l}<\/td><td>${txt||'\u2014'}<\/td><td class=\"ci-right\">${money(mi)}<\/td><td class=\"ci-right\">${money(ma)}<\/td>`;\r\n          tbody.appendChild(tr);\r\n        };\r\n        addRow('Module \/ kit','\u2014', base, base);\r\n        if(foundationRow) addRow(foundationRow.label, foundationRow.txt, foundationRow.minAmt, foundationRow.maxAmt);\r\n        rows.forEach(r=> addRow(r.label, r.txt, r.minAmt, r.maxAmt));\r\n      }\r\n\r\n      \/* --- R\u00e9sum\u00e9 & fourchettes --- *\/\r\n      const elMin = document.getElementById('simu-total-min');\r\n      const elMax = document.getElementById('simu-total-max');\r\n      if(elMin) elMin.textContent = money(totalMin);\r\n      if(elMax) elMax.textContent = money(totalMax);\r\n\r\n      const psf = document.getElementById('simu-psf');\r\n      if(psf) psf.innerHTML = `\u2248 <strong>${money(totalMin\/area)}<\/strong> \u00e0 <strong>${money(totalMax\/area)}<\/strong> \/pi\u00b2`;\r\n\r\n      const sum = document.getElementById('simu-summary');\r\n      if(sum){\r\n        sum.innerHTML =\r\n          `Superficie : ${area.toLocaleString('fr-CA')} pi\u00b2<br>\r\n           Prix module : ${money(price)}<br>\r\n           Ajustement r\u00e9gional : \u00d7${region}<br><br>\r\n           Fourchette totale : <strong>${money(totalMin)} \u2013 ${money(totalMax)}<\/strong>`;\r\n      }\r\n\r\n      document.getElementById('simu-results')?.scrollIntoView({behavior:'smooth'});\r\n    };\r\n\r\n    form.addEventListener('submit',compute,true);\r\n    const btn=form.querySelector('button[type=\"submit\"],input[type=\"submit\"]');\r\n    if(btn) btn.addEventListener('click',compute,true);\r\n  }\r\n\r\n  if(document.readyState!=='loading') waitForm(attach);\r\n  else document.addEventListener('DOMContentLoaded',()=>waitForm(attach));\r\n})();\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-8cbef5d e-flex e-con-boxed e-con e-parent\" data-id=\"8cbef5d\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-aee0e33 elementor-widget elementor-widget-html\" data-id=\"aee0e33\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\/* \u2014\u2014\u2014 Cartes (param\u00e8tres & notes) \u2014\u2014\u2014 *\/\r\n.simu-card,\r\n.simu-notes{\r\n  background:#fff!important;\r\n  border:1px solid #e5e7eb!important;\r\n  border-radius:16px!important;\r\n  padding:20px!important;\r\n  box-shadow:0 1px 2px rgba(16,24,40,.06)!important;\r\n  color:#334155!important;\r\n}\r\n\r\n\/* \u2014\u2014\u2014 Notes importantes \u2014\u2014\u2014 *\/\r\n.simu-notes{\r\n  font-size:15px!important;\r\n  line-height:1.55!important;\r\n  display:flex!important; flex-direction:column!important;\r\n}\r\n.simu-notes h4{font-size:16px!important;font-weight:800!important;margin-bottom:10px!important;color:#0b1220!important;}\r\n.simu-notes ul{margin:0!important;padding-left:18px!important;flex:1 1 auto!important;}\r\n.simu-notes li{margin-bottom:8px!important;}\r\n.simu-notes li strong{color:#0b1220!important;}\r\n.simu-notes .disclaimer{\r\n  border-top:1px dashed #e6eef7!important;margin-top:auto!important;padding-top:10px!important;\r\n  font-size:14px!important;color:#667085!important;\r\n}\r\n\r\n\/* \u2014\u2014\u2014 \u00c9galisation de hauteur des deux cartes (desktop) \u2014\u2014\u2014 *\/\r\n@media (min-width:900px){\r\n  .elementor-section.simu-eq > .elementor-container{align-items:stretch!important;}\r\n  .elementor-section.simu-eq .elementor-column > .elementor-widget-wrap{\r\n    display:flex!important; height:100%!important; flex-direction:column!important;\r\n  }\r\n  .elementor-section.simu-eq .simu-card,\r\n  .elementor-section.simu-eq .simu-notes{\r\n    display:flex!important; flex-direction:column!important; height:100%!important; margin-bottom:0!important;\r\n  }\r\n}\r\n\r\n\/* \u2014\u2014\u2014 Espacement mobile \u2014\u2014\u2014 *\/\r\n@media (max-width:899.98px){\r\n  .simu-card,.simu-notes{margin-bottom:20px!important;}\r\n}\r\n\r\n\/* ----------- TOGGLE \u00ab R\u00e9glages avanc\u00e9s \u00bb ----------- *\/\r\n.adv-toggle button{\r\n  width:100%; background:#f3f6fb; border:1px solid #e4e9f2; border-radius:8px;\r\n  color:#0f172a; font-weight:600; font-size:16px; text-align:left;\r\n  padding:10px 14px; display:flex; align-items:center; justify-content:space-between; gap:.5rem;\r\n  cursor:pointer; transition:all .25s ease;\r\n}\r\n.adv-toggle button:hover{background:#eef3fb;}\r\n.adv-toggle button .chev{transition: transform .25s ease;}\r\n.adv-toggle button[aria-expanded=\"true\"] .chev{transform:rotate(90deg);}\r\n.adv-panel{overflow:hidden; max-height:0; opacity:0; transform:translateY(-2px);\r\n  transition:max-height .35s ease, opacity .25s ease, transform .25s ease;}\r\n.adv-panel.is-open{max-height:3000px; opacity:1; transform:translateY(0);}\r\n.adv-wrapper{margin-bottom:16px;}\r\n\r\n\/* \u2014\u2014\u2014 Barre de mode \u00ab J\u2019ai le prix \/ Calculer avec pi\u00b2 \u00bb \u2014\u2014\u2014 *\/\r\n\/* l\u00e9g\u00e8re, sans fond, int\u00e9gr\u00e9e sous la 1re ligne *\/\r\n.price-mode-bar{\r\n  display:flex; align-items:center; flex-wrap:wrap; gap:16px;\r\n  margin:8px 0 6px; font-size:14px; color:#0f172a;\r\n}\r\n.price-mode-bar label{display:inline-flex; align-items:center; gap:8px; cursor:pointer;}\r\n.price-mode-bar input[type=\"radio\"]{transform:translateY(1px);}\r\n\r\n\/* Champ $\/pi\u00b2 inline \u00e0 droite quand \u00ab Calculer avec pi\u00b2 \u00bb est coch\u00e9 *\/\r\n.ppsf-inline{display:none; align-items:center; gap:8px;}\r\n.ppsf-inline .ppsf-inline__label{font-weight:700; font-size:14px;}\r\n.ppsf-inline input{\r\n  width:140px; max-width:100%; border:1px solid #e5e7eb; border-radius:10px; padding:8px 10px; font-size:14px;\r\n}\r\n.ppsf-inline .ppsf-inline__hint{font-size:12px; color:#667085; margin-left:6px}\r\n\r\n\/* \u2014\u2014\u2014 Forcer \u00ab Prix du module \u00bb & \u00ab Type de projet \u00bb sur la m\u00eame ligne \u2014\u2014\u2014 *\/\r\n@media (min-width:900px){\r\n  .jfb-inline-two {display:block;}\r\n  .jfb-inline-two > .jfb-inline {display:inline-block!important; width: calc(50% - 12px)!important; vertical-align:top!important;}\r\n  .jfb-inline-two > .jfb-inline + .jfb-inline {margin-left:24px!important;}\r\n}\r\n<\/style>\r\n\r\n<script>\r\ndocument.addEventListener('DOMContentLoaded', function () {\r\n  \/* ---------- utilitaires ---------- *\/\r\n  const $q = (s,root=document)=>root.querySelector(s);\r\n  const fieldEl = (slug)=>{\r\n    const sel = [\r\n      `[name=\"form_fields[${slug}]\"]`,\r\n      `input[name=\"form_fields[${slug}]\"]`,\r\n      `select[name=\"form_fields[${slug}]\"]`,\r\n      `[name=\"${slug}\"]`,\r\n      `[data-field-name=\"${slug}\"]`\r\n    ].join(',');\r\n    return $q(sel);\r\n  };\r\n  const rowOf = (input)=>{\r\n    if(!input) return null;\r\n    return input.closest('.jet-form-builder-row__item') || input.closest('.jet-form-builder__field') || input.parentElement;\r\n  };\r\n\r\n  \/* ---------- Toggle \u00ab R\u00e9glages avanc\u00e9s \u00bb ---------- *\/\r\n  const toggleHeading = document.querySelector('.adv-toggle');\r\n  if (toggleHeading){\r\n    const headingRow = toggleHeading.closest('.jet-form-builder-row__item') ||\r\n                       toggleHeading.closest('.jet-form-builder__field') || toggleHeading;\r\n    const label = (toggleHeading.textContent || 'R\u00e9glages avanc\u00e9s').trim();\r\n    const btn = document.createElement('button');\r\n    btn.type = 'button';\r\n    btn.setAttribute('aria-expanded', 'false');\r\n    btn.innerHTML = `<span>${label}<\/span>\r\n      <svg class=\"chev\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n        <path d=\"M9 18l6-6-6-6\" stroke=\"currentColor\" stroke-width=\"2\"\r\n              stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\r\n      <\/svg>`;\r\n    toggleHeading.innerHTML = '';\r\n    toggleHeading.appendChild(btn);\r\n\r\n    const panel = document.createElement('div');\r\n    panel.className = 'adv-panel adv-wrapper';\r\n    headingRow.parentNode.insertBefore(panel, headingRow.nextSibling);\r\n\r\n    const items = document.querySelectorAll('.adv-item');\r\n    items.forEach(el=>{\r\n      const row = el.closest('.jet-form-builder-row__item') ||\r\n                  el.closest('.jet-form-builder__field') || el;\r\n      if (row.contains(toggleHeading)) return;\r\n      panel.appendChild(row);\r\n    });\r\n\r\n    let open=false;\r\n    const setState = (v)=>{ open=v; btn.setAttribute('aria-expanded', String(v)); panel.classList.toggle('is-open', v); };\r\n    setState(false);\r\n    btn.addEventListener('click', ()=>setState(!open));\r\n  }\r\n\r\n  \/* ---------- Barre \u00ab J\u2019ai le prix \/ Calculer avec pi\u00b2 \u00bb ---------- *\/\r\n  const area = fieldEl('area');\r\n  const modulePrice = fieldEl('module_price');\r\n  const modulePriceRow = rowOf(modulePrice);\r\n  const buildType = fieldEl('build_type');\r\n  const buildTypeRow = rowOf(buildType);\r\n\r\n  if (modulePriceRow){\r\n    const bar = document.createElement('div');\r\n    bar.className = 'price-mode-bar';\r\n    bar.innerHTML = `\r\n      <label><input type=\"radio\" name=\"ci_price_mode\" value=\"direct\" checked> J'ai le prix du module<\/label>\r\n      <label><input type=\"radio\" name=\"ci_price_mode\" value=\"ppsf\"> Calculer avec superficie<\/label>\r\n      <div class=\"ppsf-inline\" id=\"ci-ppsf-inline\">\r\n        <span class=\"ppsf-inline__label\">$ \/ pi\u00b2<\/span>\r\n        <input type=\"number\" id=\"ci-ppsf\" min=\"50\" step=\"5\" value=\"180\">\r\n        <span class=\"ppsf-inline__hint\">Ex. 160\u2013220 $\/pi\u00b2<\/span>\r\n      <\/div>\r\n    `;\r\n\r\n    \/* \u2b06\ufe0f Ins\u00e8re la barre DIRECTEMENT AVANT la ligne \"Prix du module\" (au-dessus du champ) *\/\r\n    modulePriceRow.parentNode.insertBefore(bar, modulePriceRow);\r\n\r\n    const radios   = bar.querySelectorAll('input[name=\"ci_price_mode\"]');\r\n    const ppsfWrap = bar.querySelector('#ci-ppsf-inline');\r\n    const ppsfInput = bar.querySelector('#ci-ppsf');\r\n    const ppsfHint  = bar.querySelector('.ppsf-inline__hint');\r\n\r\n    \/* ---- plage de prix selon le type de projet ---- *\/\r\n    const setPpsfRange = () => {\r\n      const type = (buildType && buildType.value ? buildType.value.toLowerCase() : '');\r\n      let min, max, def, hint;\r\n\r\n      if (type.includes('kit')) {\r\n        \/\/ Kit \/ panneaux : 60\u2013120, d\u00e9faut 75\r\n        min = 40;\r\n        max = 500;\r\n        def = 120;\r\n        hint = 'Ex. 80\u2013160 $\/pi\u00b2';\r\n      } else {\r\n        \/\/ Module (ou valeur par d\u00e9faut) : 160\u2013220, d\u00e9faut 180\r\n        min = 100;\r\n        max = 500;\r\n        def = 180;\r\n        hint = 'Ex. 160\u2013220 $\/pi\u00b2';\r\n      }\r\n\r\n      ppsfInput.min = String(min);\r\n      ppsfInput.max = String(max);\r\n      ppsfHint.textContent = hint;\r\n\r\n      \/\/ on remet la valeur par d\u00e9faut de cette plage\r\n      ppsfInput.value = def;\r\n    };\r\n\r\n    const refreshMode = ()=>{\r\n      const mode = bar.querySelector('input[name=\"ci_price_mode\"]:checked')?.value || 'direct';\r\n      if(mode==='ppsf'){\r\n        ppsfWrap.style.display = 'inline-flex';\r\n        const A = parseFloat((area && area.value) || '0')||0;\r\n        const P = parseFloat(ppsfInput.value||'0')||0;\r\n        if (A>0 && P>0 && modulePrice) modulePrice.value = Math.round(A*P);\r\n      }else{\r\n        ppsfWrap.style.display = 'none';\r\n      }\r\n    };\r\n\r\n    radios.forEach(r=>r.addEventListener('change', refreshMode));\r\n    if (area) area.addEventListener('input', refreshMode);\r\n    ppsfInput.addEventListener('input', refreshMode);\r\n\r\n    \/\/ mise \u00e0 jour quand on change le type de projet\r\n    if (buildType) {\r\n      buildType.addEventListener('change', () => {\r\n        setPpsfRange();\r\n        refreshMode();\r\n      });\r\n    }\r\n\r\n    \/\/ \u00e9tat initial : plage selon type (Module par d\u00e9faut) + calcul\r\n    setPpsfRange();\r\n    refreshMode();\r\n  }\r\n\r\n  \/* ---------- M\u00eame ligne (desktop) : module_price + build_type ---------- *\/\r\n  if (modulePriceRow && buildTypeRow){\r\n    if (modulePriceRow.nextElementSibling !== buildTypeRow){\r\n      modulePriceRow.parentNode.insertBefore(buildTypeRow, modulePriceRow.nextElementSibling);\r\n    }\r\n    modulePriceRow.classList.add('jfb-inline');\r\n    buildTypeRow.classList.add('jfb-inline');\r\n    modulePriceRow.parentNode.classList.add('jfb-inline-two');\r\n  }\r\n});\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-55184f6 e-flex e-con-boxed e-con e-parent\" data-id=\"55184f6\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4c39f39 elementor-widget elementor-widget-html\" data-id=\"4c39f39\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<script>\r\n(function() {\r\n  \/\/ ---------- S\u00e9lecteur robuste ----------\r\n  function $f(slug){\r\n    return document.querySelector(\r\n      `[name=\"form_fields[${slug}]\"],[name$=\"[${slug}]\"],[name*=\"[${slug}]\"],[name=\"${slug}\"],[data-field-name=\"${slug}\"]`\r\n    );\r\n  }\r\n  const val = el => (el ? String(el.value || '').trim() : '');\r\n  const hasDims = v => \/\\d+\\s*[xX\u00d7*]\\s*\\d+\/.test(String(v||''));\r\n\r\n  \/\/ ---------- Tables de fourchettes ----------\r\n  \/\/ % (sans dimensions)\r\n  const PCT_MOD = { slab:'8-12',  crawl:'10-15', basement:'15-20', piers:'6-10'  };\r\n  const PCT_KIT = { slab:'10-14', crawl:'12-17', basement:'18-24', piers:'7-11'  };\r\n  \/\/ $\/pi\u00b2 (avec dimensions)\r\n  const PSF     = { slab:'12-18', crawl:'18-24', basement:'30-45', piers:'20-30' };\r\n\r\n  function fmtRange(r){ return String(r||'').replace('-', '\u2013'); }\r\n\r\n  \/\/ ---------- Normalisation des valeurs ----------\r\n  function normalizeBuildType(raw){\r\n    const v = String(raw||'').toLowerCase();\r\n    if (v.includes('kit')) return 'kit';\r\n    return 'module'; \/\/ d\u00e9faut\r\n  }\r\n\r\n  function normalizeFoundationType($sel){\r\n    \/\/ 1) tente la value\r\n    const v = val($sel).toLowerCase();\r\n    if (['slab','crawl','basement','piers'].includes(v)) return v;\r\n\r\n    \/\/ 2) sinon, d\u00e9duire \u00e0 partir du texte visible de l\u2019option s\u00e9lectionn\u00e9e\r\n    try{\r\n      const opt = $sel && $sel.options ? $sel.options[$sel.selectedIndex] : null;\r\n      const t = (opt ? opt.textContent : val($sel)).toLowerCase();\r\n\r\n      if (t.includes('dalle'))              return 'slab';\r\n      if (t.includes('vide') || t.includes('sanit')) return 'crawl';\r\n      if (t.includes('sous') || t.includes('fondation')) return 'basement';\r\n      if (t.includes('pieu'))               return 'piers';\r\n    }catch(e){\/* no-op *\/}\r\n\r\n    return ''; \/\/ inconnu\r\n  }\r\n\r\n  function update(){\r\n    const $out   = $f('foundation');         \/\/ champ affich\u00e9\r\n    const $dims  = $f('dims');\r\n    const $fType = $f('foundation_type');\r\n    const $bType = $f('build_type');\r\n\r\n    if (!$out || !$fType || !$bType) return; \/\/ pas encore rendu\r\n\r\n    const dimsV   = val($dims);\r\n    const fTypeV  = normalizeFoundationType($fType);\r\n    const bTypeV  = normalizeBuildType(val($bType));\r\n\r\n    let display = '';\r\n\r\n    if (hasDims(dimsV)) {\r\n      \/\/ Dimensions pr\u00e9sentes \u2192 $\/pi\u00b2\r\n      const r = PSF[fTypeV] || '';\r\n      display = r ? `${fmtRange(r)} $\/pi\u00b2` : '';\r\n    } else {\r\n      \/\/ Pas de dimensions \u2192 %\r\n      const table = (bTypeV === 'kit') ? PCT_KIT : PCT_MOD;\r\n      const r = table[fTypeV] || '';\r\n      display = r ? `${fmtRange(r)} %` : '';\r\n    }\r\n\r\n    \/\/ \u00c9crit la valeur (si le champ existe bien)\r\n    if ($out) $out.value = display;\r\n  }\r\n\r\n  function attachListeners(){\r\n    ['dims','foundation_type','build_type'].forEach(slug=>{\r\n      const el = $f(slug);\r\n      if(!el) return;\r\n      el.addEventListener('input',  update);\r\n      el.addEventListener('change', update);\r\n    });\r\n  }\r\n\r\n  \/\/ Observer pour attendre l\u2019hydratation JetEngine\r\n  const obs = new MutationObserver(()=>{ attachListeners(); update(); });\r\n  function startObserver(){\r\n    const form = document.querySelector('form.jet-form-builder, .jet-form-builder form, form[id*=\"jet-form\"], form[action*=\"jet-form\"], form[data-form-id]');\r\n    if (form){\r\n      obs.observe(form, {childList:true, subtree:true});\r\n    }\r\n  }\r\n\r\n  \/\/ Init\r\n  function init(){\r\n    startObserver();\r\n    attachListeners();\r\n    setTimeout(update, 200);\r\n  }\r\n\r\n  if (document.readyState !== 'loading') init();\r\n  else document.addEventListener('DOMContentLoaded', init);\r\n})();\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e412a70 e-flex e-con-boxed e-con e-parent\" data-id=\"e412a70\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a8be52a elementor-widget elementor-widget-html\" data-id=\"a8be52a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<script>\r\n\/* ---- Adaptateur DOM -> SIMU_LAST_RESULT ---- *\/\r\n(function(){\r\n  const num = (s) => {\r\n    if (!s) return 0;\r\n    \/\/ garde juste chiffres, . , et espaces; remplace virgule par point pour parseFloat\r\n    const cleaned = String(s).replace(\/[^\\d.,\\s]\/g, '').replace(\/\\s\/g, '').replace(',', '.');\r\n    const n = parseFloat(cleaned);\r\n    return isNaN(n) ? 0 : n;\r\n  };\r\n\r\n  \/\/ Construit l'objet r\u00e9sultat \u00e0 partir du DOM actuel (#simu-table, #simu-summary, etc.)\r\n  function buildSimuResultForCompare() {\r\n    const table = document.querySelector('#simu-table');\r\n    if (!table) return null;\r\n\r\n    const totalMin = document.querySelector('#simu-total-min')?.textContent || '';\r\n    const totalMax = document.querySelector('#simu-total-max')?.textContent || '';\r\n    const area     = document.querySelector('[name=\"area\"], #area, #champ-area')?.value || ''; \/\/ adapte si tes IDs diff\u00e8rent\r\n    const type     = document.querySelector('[name=\"type\"], #type, #champ-type')?.value || '';\r\n    const foundation = document.querySelector('[name=\"foundation\"], #foundation, #champ-foundation')?.value || '';\r\n\r\n    \/\/ label lisible pour la carte\r\n    const label = `${type || '\u2014'} \u2022 ${area || '\u2014'} pi\u00b2 \u2022 ${foundation || '\u2014'}`;\r\n\r\n    \/\/ breakdown par lignes du tbody\r\n    const breakdown = {};\r\n    document.querySelectorAll('#simu-table tbody tr').forEach(tr => {\r\n      const cells = tr.querySelectorAll('td, th');\r\n      const name = (cells[0]?.textContent || '').trim();\r\n      \/\/ tes colonnes: [Poste] [min\u2013max] [min] [max]\r\n      const minV = num(cells[2]?.textContent);\r\n      const maxV = num(cells[3]?.textContent);\r\n      if (name) breakdown[name] = { min: minV, max: maxV };\r\n    });\r\n\r\n    return {\r\n      id: 'SIM-' + Date.now(),\r\n      label,\r\n      area: area ? parseFloat(String(area).replace(',', '.')) : null,\r\n      type: type || '',\r\n      foundation: foundation || '',\r\n      totals: { min: num(totalMin), max: num(totalMax) },\r\n      breakdown\r\n    };\r\n  }\r\n\r\n  \/\/ \u00c0 appeler juste apr\u00e8s ton calcul (quand le tableau est \u00e0 jour)\r\n  window.SIMU_AFTER_CALC = function(){\r\n    const r = buildSimuResultForCompare();\r\n    if (!r) { console.warn('Comparateur: aucun r\u00e9sultat d\u00e9tect\u00e9'); return; }\r\n    window.SIMU_LAST_RESULT = r;\r\n    \/\/ d\u00e9verrouille visuellement le bouton Importer si tu veux\r\n    const imp = document.getElementById('sc-import-last');\r\n    if (imp) imp.disabled = false;\r\n  };\r\n\r\n  \/\/ Si des r\u00e9sultats sont d\u00e9j\u00e0 affich\u00e9s au chargement (ex: recalcul server-side)\r\n  window.addEventListener('DOMContentLoaded', () => {\r\n    const hasTotals = document.querySelector('#simu-total-min')?.textContent?.trim() && document.querySelector('#simu-total-min')?.textContent?.trim() !== '\u2014';\r\n    if (hasTotals) window.SIMU_AFTER_CALC();\r\n  });\r\n\r\n  \/\/ Bonus : bouton \"Ajouter au comparateur\" universel (si tu en mets un \u00e0 c\u00f4t\u00e9 des r\u00e9sultats)\r\n  document.addEventListener('click', (ev) => {\r\n    const t = ev.target.closest('[data-simu-add-compare]');\r\n    if (!t) return;\r\n    ev.preventDefault();\r\n    \/\/ s'assure que SIMU_LAST_RESULT est frais\r\n    if (!window.SIMU_LAST_RESULT) window.SIMU_AFTER_CALC();\r\n    if (window.SIMU_LAST_RESULT && window.SimuCompare?.add) {\r\n      window.SimuCompare.add(window.SIMU_LAST_RESULT);\r\n    } else {\r\n      console.warn('SimuCompare non charg\u00e9 ou r\u00e9sultat indisponible');\r\n    }\r\n  });\r\n})();\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-0010200 e-flex e-con-boxed e-con e-parent\" data-id=\"0010200\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;jet_parallax_layout_list&quot;:[]}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-ea47902 elementor-widget elementor-widget-html\" data-id=\"ea47902\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<h2 style=\"text-align:center;\">\r\n  Pr\u00eat \u00e0 b\u00e2tir pour vrai? On t\u2019accompagne \u00e9tape par \u00e9tape \u2014 gratuit.\r\n<\/h2>\r\n\r\n<iframe\r\n  id=\"zohoFormLangSwitch-cta\"\r\n  aria-label=\"On t'accompagne pour ton projet - C'EST GRATUIT\"\r\n  frameborder=\"0\"\r\n  style=\"height:875px;width:100%;border:none;\"\r\n  loading=\"eager\"\r\n  src=\"https:\/\/zfrmz.ca\/jQ7QbOtoKbQqUgikQmQI\">\r\n<\/iframe>\r\n\r\n<script>\r\ndocument.addEventListener('DOMContentLoaded', function () {\r\n  \/\/ D\u00e9tection simple de la version anglaise (\/en ou \/en\/...)\r\n  var path = window.location.pathname.toLowerCase();\r\n  var isEN = path === '\/en' || path.indexOf('\/en\/') === 0;\r\n\r\n  var FR_URL = \"https:\/\/zfrmz.ca\/jQ7QbOtoKbQqUgikQmQI\";\r\n  var EN_URL = \"https:\/\/zfrmz.ca\/jQ7QbOtoKbQqUgikQmQI?zf_lang=en\";\r\n\r\n  var iframe = document.getElementById('zohoFormLangSwitch-cta');\r\n  if (iframe) {\r\n    iframe.src = isEN ? EN_URL : FR_URL;\r\n  }\r\n});\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Estimateur de prix Calculez en quelques secondes le co\u00fbt d\u2019une maison pr\u00e9fabriqu\u00e9e au Qu\u00e9bec. Notre simulateur en ligne estime le prix total selon la superficie, le type de pr\u00e9fabrication (modulaire ou en kit) et le type de fondation. L\u2019outil d\u00e9compose automatiquement les postes cl\u00e9s \u2014 excavation, finitions, raccordements et impr\u00e9vus \u2014 selon les standards du [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":16858,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-16298","page","type-page","status-publish","has-post-thumbnail","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Simulateur de co\u00fbt pour maison pr\u00e9fabriqu\u00e9e\ud83c\udfe1<\/title>\n<meta name=\"description\" content=\"Estimez le co\u00fbt de votre maison pr\u00e9fabriqu\u00e9e au Qu\u00e9bec. Outil gratuit et rapide pour calculer le prix total d&#039;un projet pr\u00e9fab.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/collectionimmobiliere.com\/en\/cost-estimator-prefab-home\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Simulateur de co\u00fbt pour maison pr\u00e9fabriqu\u00e9e\ud83c\udfe1\" \/>\n<meta property=\"og:description\" content=\"Estimez le co\u00fbt de votre maison pr\u00e9fabriqu\u00e9e au Qu\u00e9bec. Outil gratuit et rapide pour calculer le prix total d&#039;un projet pr\u00e9fab.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/collectionimmobiliere.com\/en\/cost-estimator-prefab-home\/\" \/>\n<meta property=\"og:site_name\" content=\"Collection Immobili\u00e8re\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=61560608204872\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-12T18:03:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/Sans-titre-300-x-141-px-425-x-200-px.png\" \/>\n\t<meta property=\"og:image:width\" content=\"425\" \/>\n\t<meta property=\"og:image:height\" content=\"200\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/\",\"url\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/\",\"name\":\"Simulateur de co\u00fbt pour maison pr\u00e9fabriqu\u00e9e\ud83c\udfe1\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/collectionimmobiliere.com\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/Sans-titre-300-x-141-px-425-x-200-px.png\",\"datePublished\":\"2025-10-31T13:39:23+00:00\",\"dateModified\":\"2026-01-12T18:03:08+00:00\",\"description\":\"Estimez le co\u00fbt de votre maison pr\u00e9fabriqu\u00e9e au Qu\u00e9bec. Outil gratuit et rapide pour calculer le prix total d'un projet pr\u00e9fab.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/#breadcrumb\"},\"inLanguage\":\"en-CA\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-CA\",\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/#primaryimage\",\"url\":\"https:\\\/\\\/collectionimmobiliere.com\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/Sans-titre-300-x-141-px-425-x-200-px.png\",\"contentUrl\":\"https:\\\/\\\/collectionimmobiliere.com\\\/wp-content\\\/uploads\\\/2025\\\/10\\\/Sans-titre-300-x-141-px-425-x-200-px.png\",\"width\":425,\"height\":200,\"caption\":\"simulateur de prix pour maison et chalet pr\u00e9fabriqu\u00e9e\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/simulateur-de-cout-pour-maison-prefabriquee\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/collectionimmobiliere.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Simulateur de co\u00fbt pour maison pr\u00e9fabriqu\u00e9e\ud83c\udfe1\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/#website\",\"url\":\"https:\\\/\\\/collectionimmobiliere.com\\\/\",\"name\":\"Collection Immobili\u00e8re\",\"description\":\"L&#039;immobilier simplement expliqu\u00e9\",\"publisher\":{\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/collectionimmobiliere.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-CA\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/#organization\",\"name\":\"Collection immobili\u00e8re\",\"url\":\"https:\\\/\\\/collectionimmobiliere.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-CA\",\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/collectionimmobiliere.com\\\/wp-content\\\/uploads\\\/2022\\\/05\\\/Logo.png\",\"contentUrl\":\"https:\\\/\\\/collectionimmobiliere.com\\\/wp-content\\\/uploads\\\/2022\\\/05\\\/Logo.png\",\"width\":150,\"height\":150,\"caption\":\"Collection immobili\u00e8re\"},\"image\":{\"@id\":\"https:\\\/\\\/collectionimmobiliere.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/profile.php?id=61560608204872\",\"https:\\\/\\\/ca.pinterest.com\\\/CollectionImmobiliere\\\/\",\"https:\\\/\\\/www.instagram.com\\\/collectionimmobiliere\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/105178697\\\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Prefab Home Cost Estimator \ud83c\udfe1","description":"Estimate the price of your prefabricated or modular home in Quebec. Free and fast calculation tool \u2014 compare budgets and plan your project with clarity.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/collectionimmobiliere.com\/en\/cost-estimator-prefab-home\/","og_locale":"en_US","og_type":"article","og_title":"Simulateur de co\u00fbt pour maison pr\u00e9fabriqu\u00e9e\ud83c\udfe1","og_description":"Estimez le co\u00fbt de votre maison pr\u00e9fabriqu\u00e9e au Qu\u00e9bec. Outil gratuit et rapide pour calculer le prix total d'un projet pr\u00e9fab.","og_url":"https:\/\/collectionimmobiliere.com\/en\/cost-estimator-prefab-home\/","og_site_name":"Collection Immobili\u00e8re","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=61560608204872","article_modified_time":"2026-01-12T18:03:08+00:00","og_image":[{"width":425,"height":200,"url":"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/Sans-titre-300-x-141-px-425-x-200-px.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/","url":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/","name":"Prefab Home Cost Estimator \ud83c\udfe1","isPartOf":{"@id":"https:\/\/collectionimmobiliere.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/#primaryimage"},"image":{"@id":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/#primaryimage"},"thumbnailUrl":"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/Sans-titre-300-x-141-px-425-x-200-px.png","datePublished":"2025-10-31T13:39:23+00:00","dateModified":"2026-01-12T18:03:08+00:00","description":"Estimate the price of your prefabricated or modular home in Quebec. Free and fast calculation tool \u2014 compare budgets and plan your project with clarity.","breadcrumb":{"@id":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/#breadcrumb"},"inLanguage":"en-CA","potentialAction":[{"@type":"ReadAction","target":["https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/"]}]},{"@type":"ImageObject","inLanguage":"en-CA","@id":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/#primaryimage","url":"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/Sans-titre-300-x-141-px-425-x-200-px.png","contentUrl":"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2025\/10\/Sans-titre-300-x-141-px-425-x-200-px.png","width":425,"height":200,"caption":"simulateur de prix pour maison et chalet pr\u00e9fabriqu\u00e9e"},{"@type":"BreadcrumbList","@id":"https:\/\/collectionimmobiliere.com\/simulateur-de-cout-pour-maison-prefabriquee\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/collectionimmobiliere.com\/"},{"@type":"ListItem","position":2,"name":"Simulateur de co\u00fbt pour maison pr\u00e9fabriqu\u00e9e\ud83c\udfe1"}]},{"@type":"WebSite","@id":"https:\/\/collectionimmobiliere.com\/#website","url":"https:\/\/collectionimmobiliere.com\/","name":"Collection Immobili\u00e8re","description":"Real Estate Simply Explained","publisher":{"@id":"https:\/\/collectionimmobiliere.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/collectionimmobiliere.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-CA"},{"@type":"Organization","@id":"https:\/\/collectionimmobiliere.com\/#organization","name":"Real estate collection","url":"https:\/\/collectionimmobiliere.com\/","logo":{"@type":"ImageObject","inLanguage":"en-CA","@id":"https:\/\/collectionimmobiliere.com\/#\/schema\/logo\/image\/","url":"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2022\/05\/Logo.png","contentUrl":"https:\/\/collectionimmobiliere.com\/wp-content\/uploads\/2022\/05\/Logo.png","width":150,"height":150,"caption":"Collection immobili\u00e8re"},"image":{"@id":"https:\/\/collectionimmobiliere.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/profile.php?id=61560608204872","https:\/\/ca.pinterest.com\/CollectionImmobiliere\/","https:\/\/www.instagram.com\/collectionimmobiliere\/","https:\/\/www.linkedin.com\/company\/105178697\/"]}]}},"_links":{"self":[{"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/pages\/16298","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/comments?post=16298"}],"version-history":[{"count":0,"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/pages\/16298\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/media\/16858"}],"wp:attachment":[{"href":"https:\/\/collectionimmobiliere.com\/en\/wp-json\/wp\/v2\/media?parent=16298"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}