<!doctype html>
<html lang="fr">
  <head>
    <meta charset="UTF-8" />
    <meta name="google-site-verification" content="dLzGKDXvE2lt19AnE_TsdpdoQRpFw3evX0kG6sG2j3g" />
    <meta name="facebook-domain-verification" content="d5kppkq8ytcyv3oxa1v40682fhre7k" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
    <meta name="theme-color" content="#020C1B" />
    <!-- Favicon KWANTHIC (sphère lumineuse). Remplace l'ancien favicon
         générique. Formats multi-tailles pour navigateurs, iOS, Android
         et PWA. -->
    <link rel="icon" href="/favicon.ico" sizes="any">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="192x192" href="/favicon-192x192.png">
    <link rel="icon" type="image/png" sizes="512x512" href="/favicon-512x512.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
    <link rel="manifest" href="/manifest.webmanifest">
    <meta name="application-name" content="KWANTHIC">
    <meta name="apple-mobile-web-app-title" content="KWANTHIC">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="msapplication-TileColor" content="#020C1B">
    <meta name="msapplication-TileImage" content="/favicon-192x192.png">

    <!-- DNS prefetch for external resources -->
    <!-- PERF (cause 7/8) : préconnexion backend AVEC `crossorigin` (les appels
         sont des fetch CORS) — sans l'attribut, le handshake anticipé n'est pas
         réutilisé et PSI signale « Vérifiez l'attribut crossorigin ».
         Le dns-prefetch reste en repli pour les navigateurs sans preconnect. -->
    <link rel="preconnect" href="https://wwrfwwurtanwvalnaoxq.supabase.co" crossorigin />
    <link rel="dns-prefetch" href="https://wwrfwwurtanwvalnaoxq.supabase.co" />
    <!-- Pré-connexions analytics/pixels (économise ~150ms par origine au moment où ils chargent) -->
    <link rel="dns-prefetch" href="https://connect.facebook.net" />
    <link rel="dns-prefetch" href="https://analytics.tiktok.com" />
    <link rel="dns-prefetch" href="https://www.googletagmanager.com" />
    <link rel="dns-prefetch" href="https://plausible.io" />

    <!-- ===========================================================
         Analytics — GA4 + Plausible (RGPD-compliant via Consent Mode v2)
         GA4 démarre en mode "denied" : aucune donnée n'est envoyée
         tant que le visiteur n'a pas accepté les cookies.
         Le CookieBanner met à jour le consentement via:
           gtag('consent','update', { analytics_storage:'granted' })
         =========================================================== -->
    <!-- Amorce head (Consent Mode v2, Plausible, Meta, attribution, TikTok) :
         code externalisé dans /kw-head-boot.js.
         §PERF C — `defer` : le script n'est plus bloquant pour le rendu (il
         n'installe que des stubs et des files d'attente, rien n'en dépend
         pendant le parsing). L'ORDRE est préservé : les scripts `defer` et
         `type="module"` s'exécutent dans l'ordre du document, donc cette
         amorce reste exécutée AVANT /src/main.tsx. -->
    <script src="/kw-head-boot.js" defer></script>

    <!-- Plausible Analytics — stub synchrone, SDK chargé en différé (voir ci-dessus) -->

    <!-- ===========================================================
         Pixels publicitaires — Meta + TikTok + Google Ads
         PageView automatique sur toutes les pages.
         Lead / CompleteRegistration déclenchés depuis le code via
         src/lib/adTracking.ts (trackPixelLead, trackPixelCompleteRegistration).
         ⚠️  Remplacer META_PIXEL_ID, TIKTOK_PIXEL_ID, GOOGLE_CONVERSION_ID
              par les vraies valeurs avant la mise en ligne des campagnes.
         =========================================================== -->

    <!-- Meta Pixel Code -->
    <!-- End Meta Pixel Code -->

    <!-- ===========================================================
         KWANTHIC — Capture d'attribution (first-touch, session)
         Stocke utm_*, fbclid, gclid, ttclid, msclkid, landing_page,
         referrer, first_seen_at dans sessionStorage['kwanthic_attribution']
         et expose window.kwanthicAttribution. 1ère touche gagne.
         =========================================================== -->

    <!-- TikTok Pixel -->

    <!-- Google Ads : pas de config tant que GOOGLE_CONVERSION_ID n'est pas une vraie ID.
         À réactiver via src/lib/analytics.ts après consentement marketing. -->

    <!-- Polices auto-hébergées (Tour 3 perf) — plus de dépendance fonts.gstatic.com -->
    <!-- (preconnect same-origin supprimé : inutile, la connexion existe déjà) -->

    <!-- Self-hosted Space Grotesk (primary font) — preload latin slice for LCP -->
    <link rel="preload" as="font" type="font/woff2" href="/fonts/space-grotesk-latin.woff2" crossorigin />
    <!-- Montserrat 400 — Tailwind `font-sans` par défaut, présent au-dessus de la ligne de flottaison -->
    <link rel="preload" as="font" type="font/woff2" href="/fonts/montserrat-400.woff2" crossorigin />
    <link rel="preload" as="font" type="font/woff2" href="/fonts/montserrat-700.woff2" crossorigin />
    <!-- Secondary fonts (Montserrat 700, Orbitron display) — self-hosted latin subset -->

    <!-- LOT PERF 3 (22/08/2026) — le preload image « hero » a été SUPPRIMÉ.
         Cause racine mesurée : il pointait sur autonomie-globe-quantique.png
         (source 2,0 Mo) qui n'est peinte NULLE PART sur l'accueil (visuel
         retiré du slide 01 lors d'un lot précédent). Le navigateur téléchargeait
         donc en fetchpriority="high" une image jamais utilisée, en concurrence
         avec le vrai LCP (bloc texte du hero, mesuré à 2,3 s).
         Garde anti-récidive : scripts/verify-hero-preload-match.mjs refuse
         désormais tout preload image orphelin (asset non référencé par le bundle). -->


    <!-- Inline critical CSS for instant first paint -->
    <style>
      /* Self-hosted Space Grotesk — single variable woff2 reused for 400/700 (latin + latin-ext) */
      @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/space-grotesk-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
      @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/space-grotesk-latin.woff2') format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
      @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/space-grotesk-latin-ext.woff2') format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
      @font-face { font-family: 'Space Grotesk'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/space-grotesk-latin-ext.woff2') format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; }
      @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/montserrat-400.woff2') format('woff2'); }
      @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/montserrat-700.woff2') format('woff2'); }
      @font-face { font-family: 'Orbitron'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/orbitron-700.woff2') format('woff2'); }
      /* Polices de secours à MÉTRIQUES AJUSTÉES : le texte LCP est peint
         immédiatement dans une police système proche, puis remplacé par la
         webfont sans décalage (CLS ≈ 0). */
      @font-face { font-family: 'Montserrat Fallback'; src: local('Arial'), local('Helvetica'), local('Liberation Sans'); size-adjust: 111.5%; ascent-override: 86.9%; descent-override: 22.5%; line-gap-override: 0%; }
      @font-face { font-family: 'Grotesk Fallback'; src: local('Arial'), local('Helvetica'), local('Liberation Sans'); size-adjust: 103.5%; ascent-override: 96.5%; descent-override: 24%; line-gap-override: 0%; }
      *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
      html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
      body { font-family: 'Space Grotesk', 'Montserrat', 'Grotesk Fallback', 'Montserrat Fallback', system-ui, -apple-system, sans-serif; background: hsl(220, 50%, 3%); color: #e0f0ff; -webkit-font-smoothing: antialiased; }
      #root { min-height: 100vh; }
      .kwanthic-content-zone { opacity: 0; animation: fadeInContent 0.3s ease-out 0.1s forwards; }
      @keyframes fadeInContent { to { opacity: 1; } }
    </style>
    <title>KWANTHIC — Rénovation Énergétique & Aides 2026</title>
    <!-- ============================================================
         SEO P2 (2026-06-26) — Méta dédupliquées.
         Les balises overridées par page (description, robots, og:title,
         og:description, twitter:title, twitter:description) ont été
         retirées d'index.html : elles étaient sans `data-rh` et donc
         non-dédupliquées par react-helmet-async, ce qui faisait servir
         à chaque page DEUX versions concurrentes (la générique + celle
         de la route). Source unique désormais : Helmet par page
         (cf. src/pages/*.tsx) + DynamicHreflang pour le canonical.
         Ne PAS réintroduire ces balises ici sans concertation SEO.
         Conservés : title (Helmet dedupe nativement la balise title), og:image,
         twitter:image, og:type/locale/site_name, twitter:card/site
         (fallbacks invariants pour crawlers no-JS).
         ============================================================ -->
    <!-- Canonical is set per-route via react-helmet-async (see src/pages/*).
         A static canonical here would duplicate the Helmet one (link tags
         don't dedupe by rel), so it's intentionally omitted. -->
    <meta name="author" content="KWANTHIC" />
    <meta name="geo.region" content="FR" />
    <meta name="geo.placename" content="Paris" />
    <meta name="geo.position" content="48.8698;2.3075" />
    <meta name="ICBM" content="48.8698, 2.3075" />

    <!-- Open Graph -->
    <!-- og:title / og:description : émis UNIQUEMENT via ‹DefaultSeoTags› (Helmet).
         Source unique côté React pour éviter que le HTML prerender contienne deux
         jeux concurrents que les crawlers sociaux pourraient mélanger. -->
    <!-- og:url : émis UNIQUEMENT via ‹DynamicHreflang› / le ‹Helmet› de chaque page.
         Une valeur statique ici entrait en concurrence avec le og:url de la route
         (les crawlers pouvaient attribuer la page à la home). Ne PAS réintroduire. -->
    <meta property="og:type" content="website" />
    <meta property="og:locale" content="fr_FR" />
    <meta property="og:site_name" content="KWANTHIC" />
    <meta property="og:image" content="https://kwanthic.com/__l5e/assets-v1/3ebed972-e20b-4423-bfad-a5ddd617f6ec/kwanthic-og-image-v11.jpg">
    <meta property="og:image:secure_url" content="https://kwanthic.com/__l5e/assets-v1/3ebed972-e20b-4423-bfad-a5ddd617f6ec/kwanthic-og-image-v11.jpg">
    <meta property="og:image:type" content="image/jpeg">
    <meta property="og:image:width" content="1672">
    <meta property="og:image:height" content="941">
    <meta property="og:image:alt" content="KWANTHIC — Expert en Rénovation énergétique : maison en coupe avec pompe à chaleur, isolation, panneaux solaires, VMC, Velux, climatisation réversible">

    <!-- Twitter -->
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:site" content="@kwanthic" />
    <!-- twitter:title / twitter:description : idem, émis via ‹DefaultSeoTags›. -->
    <meta name="twitter:image" content="https://kwanthic.com/__l5e/assets-v1/3ebed972-e20b-4423-bfad-a5ddd617f6ec/kwanthic-og-image-v11.jpg">
    <meta name="twitter:image:alt" content="KWANTHIC — Expert en Rénovation énergétique d'ampleur de Logements">

    <!-- NB : pas de meta no-cache ici — les bundles Vite sont hashés
         (cache-busting automatique). Forcer no-cache détruisait le LCP
         (chaque visite re-téléchargeait tout l'app shell). -->

    <!-- JSON-LD structured data (static fallback) -->
    <!-- ⚠️ Aucune note globale / notation agrégée ne doit être ajoutée au
         JSON-LD, sous aucune forme (règle propriétaire). Donnée
         structurée trompeuse = action manuelle Google sur tout le site.
         Interdiction absolue, y compris avec un flux d'avis connecté. -->
    <!-- ⚠️ JSON-LD HomeAndConstructionBusiness retiré ici (arbitrage #3) :
         il faisait doublon avec le bloc JSON-LD émis par Index.tsx sur la
         home (deux entités business sur la même URL = signaux confus).
         Source unique côté React (Helmet via Index.tsx + GlobalSeoJsonLd),
         qui suit le BASE_URL canonique (www.kwanthic.com). -->
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "HomeAndConstructionBusiness",
      "@id": "https://kwanthic.com/#organization",
      "name": "KWANTHIC",
      "url": "https://kwanthic.com",
      "logo": "https://kwanthic.com/__l5e/assets-v1/3ebed972-e20b-4423-bfad-a5ddd617f6ec/kwanthic-og-image-v11.jpg",
      "image": "https://kwanthic.com/__l5e/assets-v1/3ebed972-e20b-4423-bfad-a5ddd617f6ec/kwanthic-og-image-v11.jpg",
      "description": "KWANTHIC, expert en rénovation énergétique de logements, en partenariat avec des experts RGE : pompe à chaleur, isolation, solaire, VMC, menuiseries. Simulation des aides 2026 et devis gratuit partout en France.",
      "telephone": "+33189724209",
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "66 Avenue des Champs-Élysées",
        "addressLocality": "Paris",
        "postalCode": "75008",
        "addressRegion": "Île-de-France",
        "addressCountry": "FR"
      },
      "geo": { "@type": "GeoCoordinates", "latitude": 48.8698, "longitude": 2.3075 },
      "areaServed": { "@type": "Country", "name": "France" },
      "sameAs": ["https://facebook.com/kwanthic"],
      "identifier": { "@type": "PropertyValue", "propertyID": "SIRET", "value": "10376999800016" }
    }
    </script>
  <script type="module" crossorigin src="/assets/index-C5z8XrjX.js"></script>
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-DBa5Rb11.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-tslib-CmEaCU22.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-radix-VBsDe0h-.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-query-BlT76nfg.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-motion-DbqnCmsJ.js">
  <link rel="modulepreload" crossorigin href="/assets/vendor-icons-BQv8lf1X.js">
  <link rel="stylesheet" crossorigin href="/assets/index-C9qZDP9Y.css">
</head>

  <body>
    <div id="root">
      <!-- Fond plein opaque uniquement : pas de blocs internes pour
           éviter tout layout shift quand React monte (CLS = 0). -->
      <div aria-hidden="true" style="min-height:100vh;min-height:100dvh;background:hsl(220,50%,3%)"></div>
    </div>

    <!-- Meta Pixel no-JS fallback retiré : il déclenche le tracking sans pouvoir
         lire le consentement (JS désactivé). Non conforme CNIL. À réactiver
         seulement si un mécanisme de consentement no-JS est mis en place. -->

    <!--
      Fallback no-JS — conformité art. L.224-114 Code de la conso.
      Lien France Rénov' + mentions légales servis dans le HTML statique
      pour TOUS les hôtes (www.kwanthic.com et app.kwanthic.com) et TOUTES
      les URL, y compris celles non pré-rendues. Visible uniquement quand
      JS est désactivé (crawlers no-JS, lecteurs basiques). Ne pollue ni
      le DOM hydraté ni le rendu visuel pour les visiteurs normaux.
    -->
    <noscript>
      <div style="position:fixed;left:0;right:0;bottom:0;z-index:9999;padding:8px 12px;background:hsl(222,55%,4%);color:#bfeaff;font:12px/1.4 system-ui,sans-serif;text-align:center;border-top:1px solid rgba(0,255,255,.18)">
        Service public de la rénovation énergétique :
        <a href="https://france-renov.gouv.fr" rel="noopener noreferrer" style="color:#7feaff">France Rénov'</a>
        · <a href="/mentions-legales" style="color:#bfeaff">Mentions légales</a>
        · <a href="/politique-de-confidentialite" style="color:#bfeaff">Confidentialité</a>
        · <a href="/cgv" style="color:#bfeaff">CGV</a>
      </div>
    </noscript>

    <!-- Chunk-load failure recovery: hard reload once if a stale cached index.html references deleted JS bundles -->
    <script>
      (function () {
        // Purge immédiate de l'ancien flag de debug persistant (legacy).
        // Avant avril 2026, le flag `kwanthic_coherence_debug` était lu
        // depuis localStorage et restait actif pour toujours, ce qui
        // faisait surgir les panneaux de debug à chaque navigation.
        // Désormais ce flag n'est lu que depuis sessionStorage : on
        // efface ici toute valeur résiduelle pour les visiteurs qui
        // l'avaient activée sur leur poste.
        try { localStorage.removeItem("kwanthic_coherence_debug"); } catch (e) {}
        window.__KWAN_HYDRATED__ = false;
        window.addEventListener("error", function (e) {
          var msg = (e && (e.message || "")) + "";
          if (/Loading chunk|Failed to fetch dynamically imported module|ChunkLoadError|MIME type/i.test(msg)) {
            if (!sessionStorage.getItem("kwan_reloaded")) {
              sessionStorage.setItem("kwan_reloaded", "1");
              location.reload();
            }
          }
        });
        // Clear the reload guard once hydration succeeds
        var clearGuard = setInterval(function () {
          if (window.__KWAN_HYDRATED__) {
            sessionStorage.removeItem("kwan_reloaded");
            clearInterval(clearGuard);
          }
        }, 500);
      })();
    </script>
  </body>
</html>
