<!doctype html>
<html lang="en">
  <head>
    <!-- CRITICAL: Charset MUST be in first 1024 bytes before any scripts -->
    <meta charset="UTF-8" />
    
    <!-- Theme bootstrap -->
    <script>
      (function() {
        try {
          const theme = localStorage.getItem('user-theme') || 'default';
          if (theme !== 'default') {
            const themes = {
              synthwave: 'theme-synthwave bg-synthwave-grid font-orbitron',
              noir: 'theme-noir font-serif',
              neon: 'theme-neon font-mono',
              ancient: 'theme-ancient bg-ancient-papyrus font-serif',
              mech: 'theme-mech bg-mech-circuit font-sans',
              arcade: 'theme-arcade font-mono',
              omnic: 'theme-omnic font-sans',
              lorebook: 'theme-lorebook font-serif'
            };
            if (themes[theme]) {
              document.documentElement.className = themes[theme];
            }
          }
        } catch (e) {
          console.error('Failed to apply theme from localStorage', e);
        }
      })();
    </script>
    <link rel="icon" type="image/x-icon" href="/assets/favicon-DBZJiyS6.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
    <!-- OPTIMIZED: Resource hints for critical path only -->
    <!-- Preconnect to critical APIs -->
    <link rel="preconnect" href="https://stadiumbuilds.io">
    <link rel="preconnect" href="https://api.stadiumbuilds.io">
    <link rel="preconnect" href="https://qkdvetofbsoynkfprlos.supabase.co" crossorigin>
    
    <link rel="dns-prefetch" href="https://o450942899127040.ingest.us.sentry.io">
    <link rel="dns-prefetch" href="https://www.googletagmanager.com">
    <link rel="dns-prefetch" href="https://www.google-analytics.com">
    <link rel="preconnect" href="https://cdn.intergient.com" crossorigin>
    <link rel="dns-prefetch" href="https://cdn.intergient.com">
    
    <!-- Critical API endpoints for new homepage -->
    <link rel="preconnect" href="https://stadiumbuilds.io">
    <link rel="dns-prefetch" href="https://stadiumbuilds.io">
    
    <!-- Critical fonts: 400 for body, 600 for subheadings, 700 for h1/h2 headings -->
    <!-- All three weights appear in the first viewport — preload to prevent FOUT -->
    <link rel="preload" href="/fonts/inter-400.woff2" as="font" type="font/woff2" crossorigin>
    <link rel="preload" href="/fonts/inter-600.woff2" as="font" type="font/woff2" crossorigin>
    <link rel="preload" href="/fonts/inter-700.woff2" as="font" type="font/woff2" crossorigin>

    
    <style>
      /* OPTIMIZED: Critical font faces with fallback metrics */
      @font-face {
        font-family: Inter;
        font-style: normal;
        font-weight: 400;
        font-display: swap;
        src: url('/fonts/inter-400.woff2') format('woff2');
        /* Prevent layout shift during font swap */
        size-adjust: 100%;
        ascent-override: 90%;
        descent-override: 22%;
        line-gap-override: 0%;
      }
      @font-face {
        font-family: Inter;
        font-style: normal;
        font-weight: 600;
        font-display: swap;
        src: url('/fonts/inter-600.woff2') format('woff2');
        size-adjust: 100%;
        ascent-override: 90%;
        descent-override: 22%;
        line-gap-override: 0%;
      }
      @font-face {
        font-family: Inter;
        font-style: normal;
        font-weight: 700;
        font-display: swap;
        src: url('/fonts/inter-700.woff2') format('woff2');
        size-adjust: 100%;
        ascent-override: 90%;
        descent-override: 22%;
        line-gap-override: 0%;
      }
      :root {
        --font-inter: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
        --hdr-h:64px;
        --hero-h:420px;
      }
      html, body { font-family: var(--font-inter); -webkit-text-size-adjust: 100%; }
      
      .header-critical{position:fixed;top:0;left:0;right:0;height:var(--hdr-h);z-index:50;background: rgba(15,23,42,.95); backdrop-filter: blur(8px);}
      .nav-critical { display: flex; align-items: center; justify-content: space-between; height: 100%; padding: 0 1rem; }
      .hero-claim{height:var(--hero-h)}
      
      /* CRITICAL CSS: Inline hero section styles for LCP optimization */
      .hero-section-critical {
        position: relative;
        padding: 4rem 1rem 6rem;
        text-align: center;
        overflow: hidden;
        background: linear-gradient(135deg, rgb(15 23 42), rgb(30 41 59), rgb(0 0 0));
        min-height: 500px;
      }
      .hero-title-critical {
        font-size: clamp(2.5rem, 8vw, 8rem);
        font-weight: 900;
        margin-bottom: 2rem;
        line-height: 1.1;
        background: linear-gradient(to right, rgb(253 186 116), rgb(249 115 22), rgb(239 68 68));
        background-clip: text;
        -webkit-background-clip: text;
        /*
         * LCP FIX: Use a visible fallback color instead of transparent.
         * Chrome's LCP algorithm ignores text with color:transparent, causing
         * it to skip this element as an LCP candidate. The gradient background-clip
         * still renders the visual gradient on top — the color property acts as
         * the fallback when background-clip:text is unsupported, and as the
         * LCP-detected "ink color" in Chrome's paint timing.
         * orange-500 (rgb 249 115 22) matches the gradient's dominant color.
         */
        color: rgb(249 115 22);
        text-shadow: 0 0 30px rgba(255,165,0,0.5);
      }
      .hero-subtitle-critical {
        font-size: clamp(1.25rem, 4vw, 3rem);
        color: rgb(226 232 240);
        margin-bottom: 3rem;
        font-weight: 500;
        text-shadow: 0 2px 8px rgba(0,0,0,0.7);
      }
      .hero-cta-critical {
        display: inline-flex;
        align-items: center;
        gap: 1rem;
        padding: 1.5rem 4rem;
        background: linear-gradient(to right, rgb(249 115 22), rgb(239 68 68), rgb(249 115 22));
        color: white;
        font-weight: 900;
        font-size: 1.5rem;
        border-radius: 1rem;
        text-decoration: none;
        transition: all 0.3s;
        box-shadow: 0 10px 40px rgba(255,165,0,0.4);
      }
      .hero-cta-critical:hover { transform: scale(1.05); }
      
      /* WCAG 2.3.3: Respect prefers-reduced-motion for critical CSS */
      @media (prefers-reduced-motion: reduce) {
        .hero-cta-critical {
          transition: none !important;
        }
        .hero-cta-critical:hover {
          transform: none !important;
        }
      }
      
      @media (max-width:768px){
        :root{ --hdr-h:64px; --hero-h:420px; }
        .hero-section-critical { padding: 2rem 1rem 4rem; min-height: 400px; }
        .hero-title-critical { font-size: clamp(2rem, 10vw, 4rem); margin-bottom: 1.5rem; }
        .hero-subtitle-critical { font-size: clamp(1rem, 5vw, 1.5rem); margin-bottom: 2rem; }
        .hero-cta-critical { padding: 1rem 2rem; font-size: 1.25rem; }
      }
      
      /*
       * CLS FIX: #static-hero MUST be position:fixed so it does NOT occupy
       * document flow. If it were in flow, hiding it with display:none would
       * collapse its space (~500px), causing all React-rendered content below
       * to jump up — producing a massive CLS of ~0.616.
       *
       * Fixed positioning means the hero overlays content visually but takes
       * zero layout space. When display:none is applied by app:ready, no
       * elements shift. The z-index:1 keeps it above the React root while it
       * is visible, and the top/left/right values align it with the viewport.
       *
       * The React root renders underneath it (z-index:0) and is instantly
       * visible to the browser even while the hero overlay is shown.
       */
      #static-hero {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1;
        overflow: hidden;
      }
    </style>
    
    <!-- Defer main stylesheet -->
    <noscript></noscript>
    
    <!-- Conditional theme stylesheet + Google Fonts preload -->
    <!--
      PERFORMANCE FIX: Google Fonts for non-default themes are loaded HERE,
      conditionally, only when the user has actually selected that theme.
      Previously all 9 font families were unconditionally imported in themes.css,
      causing 9 extra HTTP requests for every user including default theme users.
      Now: default theme users load ZERO Google Fonts.
    -->
    <script>
      (function() {
        try {
          const theme = localStorage.getItem('user-theme') || 'default';

          // Map of theme → Google Fonts URL needed for that theme
          // Only loaded when that specific theme is active
          var THEME_FONTS = {
            synthwave:  'https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap',
            noir:       'https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap',
            neon:       'https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;700&display=swap',
            ancient:    'https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&family=Cinzel+Decorative:wght@400;700&display=swap',
            mech:       'https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;400;500;600;700&display=swap',
            arcade:     'https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap',
            omnic:      'https://fonts.googleapis.com/css2?family=Exo+2:wght@300;400;500;600;700&display=swap',
            lorebook:   'https://fonts.googleapis.com/css2?family=Crimson+Text:ital,wght@0,400;0,600;1,400&family=Playfair+Display:wght@400;700;900&family=Source+Code+Pro:wght@300;400;500;600&display=swap'
          };

          if (theme !== 'default') {
            // Load theme-specific Google Fonts if mapped
            var fontUrl = THEME_FONTS[theme];
            if (fontUrl) {
              // Preconnect to Google Fonts for faster font load
              var pc = document.createElement('link');
              pc.rel = 'preconnect';
              pc.href = 'https://fonts.googleapis.com';
              document.head.appendChild(pc);
              var pc2 = document.createElement('link');
              pc2.rel = 'preconnect';
              pc2.href = 'https://fonts.gstatic.com';
              pc2.crossOrigin = 'anonymous';
              document.head.appendChild(pc2);

              // Load the font CSS — non-blocking via onload swap pattern
              var fontLink = document.createElement('link');
              fontLink.rel = 'preload';
              fontLink.as = 'style';
              fontLink.href = fontUrl;
              fontLink.onload = function() { this.onload = null; this.rel = 'stylesheet'; };
              document.head.appendChild(fontLink);
            }

            const themeLink = document.createElement('link');
            themeLink.rel = 'preload';
            themeLink.as = 'style';
            themeLink.href = `/src/styles/themes/${theme}.css`;
            themeLink.onload = function() {
              this.onload = null;
              this.rel = 'stylesheet';
            };
            document.head.appendChild(themeLink);
            
            const noScript = document.createElement('noscript');
            const fallbackLink = document.createElement('link');
            fallbackLink.rel = 'stylesheet';
            fallbackLink.href = `/src/styles/themes/${theme}.css`;
            noScript.appendChild(fallbackLink);
            document.head.appendChild(noScript);
          }
        } catch (e) {
          console.warn('Theme CSS loading failed:', e);
        }
      })();
    </script>
    
    <!-- Primary Meta Tags -->
    <title>Stadium Builds – Overwatch Stadium Mode Strategies & Hero Guides</title>
    <meta name="title" content="Stadium Builds – Overwatch Stadium Mode Strategies & Hero Guides">
    <meta name="description" content="Browse the best Overwatch Stadium builds, explore hero dashboards, and see weekly top picks. Join thousands of players optimizing their Stadium Mode strategies.">
    
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "Organization",
      "name": "Stadium Builds",
      "url": "https://stadiumbuilds.io",
      "logo": "https://api.stadiumbuilds.io/storage/v1/object/public/images/identity/logo.png",
      "sameAs": [
        "https://discord.gg/stadiumbuildsio",
        "https://twitter.com/stadiumbuildsio"
      ]
    }
    </script>
    
    <!-- Open Graph / Twitter -->
    <meta property="og:type" content="website">
    <meta property="og:url" content="https://stadiumbuilds.io/">
    <meta property="og:title" content="Stadium Builds – Overwatch Stadium Mode Strategies & Hero Guides">
    <meta property="og:description" content="Browse the best Overwatch Stadium builds, explore hero dashboards, and see weekly top picks. Join thousands of players optimizing their Stadium Mode strategies.">
    <meta property="og:image" content="https://api.stadiumbuilds.io/storage/v1/object/public/images/banners/banner.webp">
    <meta property="twitter:card" content="summary_large_image">
    <meta property="twitter:url" content="https://stadiumbuilds.io/">
    <meta property="twitter:title" content="Stadium Builds – Overwatch Stadium Mode Strategies & Hero Guides">
    <meta property="twitter:description" content="Browse the best Overwatch Stadium builds, explore hero dashboards, and see weekly top picks. Join thousands of players optimizing their Stadium Mode strategies.">
    <meta property="twitter:image" content="https://api.stadiumbuilds.io/storage/v1/object/public/images/banners/banner.webp">
    
    <!-- SEO / PWA -->
    <meta name="robots" content="index, follow">
    <meta name="keywords" content="overwatch stadium mode, overwatch builds, hero guides, stadium builds, overwatch strategies, overwatch meta, hero dashboards">
    <meta name="author" content="Stadium Builds">
    <link rel="manifest" href="/manifest.json">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="apple-mobile-web-app-title" content="Stadium Builds">
    <link rel="apple-touch-icon" href="/assets/apple-touch-icon-B03lCFcD.png">
    <meta name="msapplication-TileColor" content="#1e3a8a">
    <meta name="msapplication-TileImage" content="/android-chrome-192x192.png">
    <meta name="theme-color" content="#1e3a8a">
    <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0f172a">
    <meta name="application-name" content="Stadium Builds">
    
    <!-- Playwire RAMP script is loaded dynamically by src/components/ads/Ramp.tsx -->
    <!-- PUB_ID: 1025752 | WEB_ID: 77245 -->
    <!-- Script URL: https://cdn.intergient.com/1025752/77245/ramp.js -->
    <script type="module" crossorigin src="/assets/index-COHLmzYg.js"></script>
    <link rel="modulepreload" crossorigin href="/assets/react-core-BgEjFJyq.js">
    <link rel="modulepreload" crossorigin href="/assets/i18n-D1NfDwlJ.js">
    <link rel="modulepreload" crossorigin href="/assets/data-layer-B9BchScJ.js">
    <link rel="modulepreload" crossorigin href="/assets/charts-OVh5kulM.js">
    <link rel="modulepreload" crossorigin href="/assets/animations-hVWsbOK6.js">
    <link rel="modulepreload" crossorigin href="/assets/markdown-B5YYWWBK.js">
    <link rel="modulepreload" crossorigin href="/assets/icons-q0HVtXd6.js">
    <link rel="stylesheet" crossorigin href="/assets/index-BCR1Z2hY.css">
  </head>
  <body style="min-height: 100vh;">
    <!-- Static header + hero outside #root for instant paint -->
    <header id="header-stub" class="header-critical">
      <nav class="nav-critical">
        <strong>Stadium Builds</strong>
        <a href="/browse">Browse</a>
      </nav>
    </header>

    <!--
      CRITICAL LCP FIX: #static-hero is now a SIBLING of #root, not a child.
      When React mounts and calls createRoot(#root).render(...), it only
      reconciles the #root subtree. This element is untouched by React's
      first render, so the H1 text stays visible until app:ready fires.

      Previously it was inside #root — React's first reconciliation replaced
      it with the isReady:false placeholder div, causing a blank flash and
      pushing the LCP candidate to after full hydration (~4.9s).
    -->
    <div class="hero-section-critical" id="static-hero">
      <h1 class="hero-title-critical">
        DISCOVER THE BEST<br>
        OVERWATCH<br>
        STADIUM BUILDS
      </h1>
      <p class="hero-subtitle-critical">
        Explore community-tested strategies for every hero<br>
        <strong style="color: rgb(34 211 238); font-size: 1.2em;">⚡ UPDATED DAILY ⚡</strong>
      </p>
      <a href="#hero-directory" class="hero-cta-critical">
        🎮 BROWSE ALL HEROES
      </a>
    </div>
    
    <div id="root"></div>
    
    
    <!-- Hide static hero when React app loads -->
    <script>
    (function(){
      window.addEventListener('app:ready', function(){
        var staticHero = document.getElementById('static-hero');
        if (staticHero) {
          staticHero.style.display = 'none';
          console.log('[Performance] Static hero hidden - React app ready');
        }
      });
    })();
    </script>
    
    <!-- Hide header stub when app is ready -->
    <script>
    (function(){
      var headerStub = document.getElementById('header-stub');
      window.addEventListener('app:ready', function(){
        if (headerStub) headerStub.style.visibility = 'hidden';
      });
    })();
    </script>
    
    <!-- After-LCP orchestrator: analytics (+1s) -->
    <script>
    (function(){
      function idle(fn){ 'requestIdleCallback' in window ? requestIdleCallback(fn,{timeout:2000}) : setTimeout(fn, 0); }
      
      // Define afterLCP function to wait for Largest Contentful Paint
      function afterLCP(callback) {
        if ('PerformanceObserver' in window && 'PerformanceEventTiming' in window) {
          var lcpDone = false;
          var observer = new PerformanceObserver(function(list) {
            var entries = list.getEntries();
            var lastEntry = entries[entries.length - 1];
            if (lastEntry && !lcpDone) {
              lcpDone = true;
              observer.disconnect();
              idle(callback);
            }
          });
          try {
            observer.observe({ type: 'largest-contentful-paint', buffered: true });
            // Fallback: if no LCP after 5s, run anyway
            setTimeout(function() {
              if (!lcpDone) {
                lcpDone = true;
                observer.disconnect();
                callback();
              }
            }, 5000);
          } catch (e) {
            // Fallback for browsers that don't support LCP
            console.warn('[Performance] LCP observer failed, loading immediately', e);
            callback();
          }
        } else {
          // Fallback for older browsers
          setTimeout(callback, 2000);
        }
      }
      
      function loadGA4(){
        try{
          window.dataLayer = window.dataLayer || [];
          function gtag(){ dataLayer.push(arguments); }
          window.gtag = gtag;

          // REVENUE-OPTIMIZED Regional Consent Strategy
          
          // EEA/UK ONLY: Strict opt-in (DENY by default until user consents)
          // GDPR requires explicit consent before tracking
          gtag('consent', 'default', {
            ad_storage:'denied',
            analytics_storage:'denied',
            ad_user_data:'denied',
            ad_personalization:'denied',
            wait_for_update:500  // Wait for CMP in EEA only
          },{
            region:['AT','BE','BG','HR','CY','CZ','DK','EE','FI','FR','DE','GR','HU','IE','IT','LV','LT','LU','MT','NL','PL','PT','RO','SK','SI','ES','SE','IS','LI','NO','GB']
          });

          // US & REST OF WORLD: GRANT EVERYTHING for maximum revenue
          // No legal requirement for opt-in consent outside EEA
          // This maximizes ad targeting, CPMs, and load speed
          gtag('consent', 'default', {
            ad_storage:'granted',             // ✅ GRANT - Critical for ad targeting & revenue
            analytics_storage:'granted',      // ✅ GRANT - Full analytics tracking
            ad_user_data:'granted',           // ✅ GRANT - Better ad personalization
            ad_personalization:'granted',     // ✅ GRANT - Higher CPMs with personalized ads
            // NO wait_for_update - Load ads immediately for max speed
          });

          // Synchronously mark analytics consent for non-EEA users.
          // Set BEFORE the async GTM script loads so AnalyticsService can read
          // this flag on module initialization — no race condition with the React bundle.
          // EEA users are intentionally excluded: their flag is set only after the TCF
          // bridge fires 'consent:analytics:granted' below.
          window.__analyticsConsentGranted = true;

          var g = document.createElement('script');
          g.async = true;
          g.src = 'https://www.googletagmanager.com/gtag/js?id=G-T01B2H7Q05';
          g.onload = function(){
            gtag('js', new Date());
            gtag('config','G-T01B2H7Q05', {
            // Google Signals: enabled to allow cross-device audience modeling,
            // demographic reporting, and higher-CPM ad targeting via Google's
            // demand-side. For EEA users with denied consent, Google Signals
            // are automatically suppressed by Consent Mode — no GDPR risk.
            allow_google_signals:true,
            transport_type:'beacon',
            
            // Consent Mode v2 Enhancements - Maximize data while respecting privacy
            ads_data_redaction:true,        // Remove personal data from URLs when consent denied
            url_passthrough:false,          // Don't pass URL params when denied
            anonymize_ip:true,              // Anonymize IPs (GDPR best practice)
            
            // Ad personalization signals: enabled globally; Consent Mode v2
            // automatically gates this for EEA users who have not consented.
            // Enabling unlocks higher CPMs from personalized ad buyers.
            allow_ad_personalization_signals:true,
            
            // CRITICAL: false prevents the automatic hard-load page_view that
            // would double-count with RouteTracker (src/components/RouteTracker.tsx).
            // RouteTracker is the single canonical source of page_view events for
            // both the initial load and all SPA navigations.
            send_page_view:false,
            cookie_flags:'SameSite=None;Secure'
          });
            
            console.log('[Analytics] GA4 initialized with consent modeling & Google Signals');

            // Notify analytics service that GA4 is ready
            window.dispatchEvent(new Event('analytics:ga4Loaded'));

            // Non-EEA consent initialization: window.__analyticsConsentGranted was set
            // synchronously above (before this GTM script even loaded), so GA4 config
            // and app consent state are aligned from the same source — no heuristic,
            // no polling, no race condition. Dispatch the event now that GA4 is ready
            // so AnalyticsService flushes any events queued before bundle init.
            window.dispatchEvent(new Event('consent:analytics:granted'));
            console.log('[Consent] ✅ Non-EEA: consent:analytics:granted dispatched (pre-granted)');

            // Enhanced TCF API Bridge - Process consent earlier & more efficiently
            (function waitForTCF(){
              if (typeof window.__tcfapi === 'function') {
                console.log('[Consent] TCF API detected, listening for consent events');
                
                window.__tcfapi('addEventListener', 2, function(tcData, ok){
                  if (!ok || !tcData) return;

                  // Process consent as soon as CMP is ready OR after user action
                  // This catches both initial load AND user choices
                  if (tcData.eventStatus === 'cmpuishown' ||
                      tcData.eventStatus === 'tcloaded' ||
                      tcData.eventStatus === 'useractioncomplete') {
                    
                    var consents = (tcData.purpose && tcData.purpose.consents) || {};
                    var legitimateInterests = (tcData.purpose && tcData.purpose.legitimateInterests) || {};
                    
                    // Purpose 1 = storage access (minimum for analytics)
                    var analyticsOK = !!consents['1'] || !!legitimateInterests['1'];

                    // Grant analytics if user has consented OR if legitimate interest applies
                    if (analyticsOK) {
                      gtag('consent','update',{ analytics_storage:'granted' });
                      console.log('[Consent] ✅ Analytics storage granted');
                      // Set the authoritative consent flag and dispatch the event.
                      // AnalyticsService subscribes to 'consent:analytics:granted' and
                      // flushes its event queue when it fires — no direct coupling to
                      // window.analyticsService.updateConsent() needed.
                      window.__analyticsConsentGranted = true;
                      window.dispatchEvent(new Event('consent:analytics:granted'));
                      console.log('[Consent] ✅ consent:analytics:granted dispatched (EEA TCF)');
                    }

                    // Grant ad signals only if full consent (purposes 1 + 3 + 4)
                    // Purpose 3 = Create personalized ads profile
                    // Purpose 4 = Select personalized ads
                    var adsOK = (consents['1'] && consents['3'] && consents['4']) ||
                                (legitimateInterests['1'] && legitimateInterests['3'] && legitimateInterests['4']);
                    
                    if (adsOK) {
                      gtag('consent','update',{
                        ad_storage:'granted',
                        ad_user_data:'granted',
                        ad_personalization:'granted'
                      });
                      console.log('[Consent] ✅ Ad storage granted (full consent)');
                      // Update AnalyticsService with full ad consent state
                      if (window.analyticsService && typeof window.analyticsService.updateConsent === 'function') {
                        window.analyticsService.updateConsent(true, true);
                        console.log('[Consent] ✅ AnalyticsService updated with full ad consent');
                      }
                    }
                    
                    console.log('[Consent] TCF processed:', {
                      event: tcData.eventStatus,
                      analytics: analyticsOK,
                      ads: adsOK,
                      purposes: consents,
                      legitimateInterests: legitimateInterests
                    });
                  }
                });
              } else {
                // TCF not available yet, retry
                setTimeout(waitForTCF, 200);
              }
            })();
          };
          document.head.appendChild(g);
        } catch(e){ console.error('[Analytics] GA4 load failed', e); }
      }
            
      // ANALYTICS FIX: Load GA4 immediately — no LCP delay, no setTimeout.
      //
      // Previously loadGA4() was wrapped in afterLCP(fn){ setTimeout(fn, 1000) },
      // which meant GA4 wasn't ready until 3-4 seconds after navigation. Users who
      // bounced within that window produced zero GA4 hits, explaining ~25-35% of
      // the gap between Similar Web (panel-based) and GA4 (tag-based) pageview counts.
      //
      // The GTM script tag has async=true so it never blocks rendering or LCP.
      // window.dataLayer is pre-initialised synchronously above, so any gtag()
      // calls made before the script loads are safely queued and replayed.
      loadGA4();
    })();
    </script>
  </body>
</html>