// ── JT Atelier — App ─────────────────────────────────────────────────

const FONT_PAIRS = {
  editorial: { display: "'GFS Didot', 'Bodoni Moda', serif",  body: "'DM Sans', system-ui, sans-serif", label: 'Bodoni' },
  instrument:{ display: "'GFS Didot', 'Instrument Serif', serif", body: "'DM Sans', system-ui, sans-serif", label: 'Instrument' },
  classic:   { display: "'GFS Didot', 'Cormorant Garamond', serif", body: "'DM Sans', system-ui, sans-serif", label: 'Classic' },
  brutalist: { display: "'Archivo Black', sans-serif",         body: "'Work Sans', system-ui, sans-serif", label: 'Brutalist' },
};

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "heroVariant": "centered",
  "fontPair": "editorial",
  "density": "regular",
  "particles": 1,
  "grain": 1,
  "dark": true
}/*EDITMODE-END*/;

function OrderConfirmation({ orderRef, goTo }) {
  return (
    <section style={{ maxWidth: 720, margin: '0 auto', padding: '120px 24px 140px', textAlign: 'center' }}>
      <div className="eyebrow" style={{ marginBottom: 22 }}>◇ &nbsp; Order confirmed</div>
      <h1 className="display" style={{ fontSize: 'clamp(48px, 9vw, 80px)', lineHeight: 1.02, margin: 0 }}>
        Thank <em>you.</em>
      </h1>
      <p style={{ fontSize: 15, lineHeight: 1.7, color: 'var(--fg-2)', margin: '22px auto 0', maxWidth: 520 }}>
        Your payment is confirmed and your order is in. A receipt is on its way to your inbox.
        Each piece is made to order in our atelier — we'll be in touch as yours moves through production.
      </p>
      {orderRef ? (
        <div className="mono" style={{ marginTop: 28, color: 'var(--fg-3)', fontSize: 11, letterSpacing: '.16em' }}>
          Order reference&nbsp;·&nbsp;<span style={{ color: 'var(--fg)' }}>{orderRef}</span>
        </div>
      ) : null}
      <div style={{ marginTop: 40, display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
        <button className="btn" onClick={() => goTo('collection')}>Continue shopping <span className="arrow">→</span></button>
        <button className="btn ghost" onClick={() => goTo('home')}>Back home</button>
      </div>
    </section>
  );
}

// ── URL routing helpers ───────────────────────────────────────────────
function parseRoute() {
  const path   = window.location.pathname;
  const search = new URLSearchParams(window.location.search);
  const segments = path.replace(/^\//, '').split('/');
  const base = segments[0] || '';

  if (base === 'collection') {
    return {
      screen: 'collection',
      gender:   search.get('gender')   || null,
      category: search.get('cat')      || null,
      season:   search.get('season')   || null,
    };
  }
  if (base === 'product') {
    return { screen: 'product', productId: segments[1] || null };
  }
  if (base === 'about')   return { screen: 'about' };
  if (base === 'contact') return { screen: 'contact' };
  if (base === 'affiliate-apply') return { screen: 'affiliate-apply' };
  if (base === 'affiliate-portal') return { screen: 'affiliate-portal' };
  if (base === 'account') {
    if (segments[1] === 'verify') return { screen: 'account-verify' };
    return { screen: 'account' };
  }
  if (base === 'collections') return { screen: 'collections', collectionSlug: segments[1] || null };
  if (base === 'journal') return { screen: 'journal' };
  if (base === 'faq')     return { screen: 'faq' };
  if (base === 'returns') return { screen: 'returns' };
  if (base === 'sizing')  return { screen: 'sizing' };
  if (base === 'survey')  return { screen: 'survey' };
  if (base === 'luxury')  return { screen: 'collection', season: 'Luxury' };
  if (base === 'bespoke') return { screen: 'collection', season: 'Bespoke' };
  if (base === 'privacy') return { screen: 'privacy' };
  if (base === 'shipping') return { screen: 'shipping' };
  if (base === 'terms')   return { screen: 'terms' };
  return { screen: 'home' };
}

function buildUrl(screen, opts = {}) {
  if (screen === 'home')    return '/';
  if (screen === 'about')   return '/about';
  if (screen === 'contact') return '/contact';
  if (screen === 'affiliate-apply')  return '/affiliate-apply';
  if (screen === 'affiliate-portal') return '/affiliate-portal';
  if (screen === 'account')          return '/account' + (opts.tab ? '?tab=' + opts.tab : '');
  if (screen === 'account-verify')   return '/account/verify';
  if (screen === 'confirmation') return '/';
  if (screen === 'collection') {
    if (opts.season === 'Luxury')  return '/luxury';
    if (opts.season === 'Bespoke') return '/bespoke';
    const p = new URLSearchParams();
    if (opts.gender)   p.set('gender', opts.gender);
    if (opts.category) p.set('cat', opts.category);
    if (opts.season)   p.set('season', opts.season);
    const qs = p.toString();
    return '/collection' + (qs ? '?' + qs : '');
  }
  if (screen === 'product' && opts.product) {
    return '/product/' + opts.product.id;
  }
  if (screen === 'collections') return '/collections' + (opts.collectionSlug ? '/' + opts.collectionSlug : '');
  if (screen === 'journal') return '/journal';
  if (screen === 'faq')     return '/faq';
  if (screen === 'returns') return '/returns';
  if (screen === 'sizing')  return '/sizing';
  if (screen === 'survey')  return '/survey';
  if (screen === 'privacy') return '/privacy';
  if (screen === 'shipping') return '/shipping';
  if (screen === 'terms')   return '/terms';
  return '/';
}

// ── SEO: per-route document head (title, meta description, canonical, Open
// Graph, and page-level JSON-LD). Google renders this JS, so per-route values
// here drive search snippets and rich results. Purely additive DOM writes.
function _seoMetaName(name, content){
  let el = document.head.querySelector('meta[name="' + name + '"]');
  if (!el) { el = document.createElement('meta'); el.setAttribute('name', name); document.head.appendChild(el); }
  el.setAttribute('content', content);
}
function _seoMetaProp(prop, content){
  let el = document.head.querySelector('meta[property="' + prop + '"]');
  if (!el) { el = document.createElement('meta'); el.setAttribute('property', prop); document.head.appendChild(el); }
  el.setAttribute('content', content);
}
function _seoCanonical(href){
  let el = document.head.querySelector('link[rel="canonical"]');
  if (!el) { el = document.createElement('link'); el.setAttribute('rel', 'canonical'); document.head.appendChild(el); }
  el.setAttribute('href', href);
}
function _seoPageJsonLd(obj){
  let el = document.getElementById('jsonld-page');
  if (!obj) { if (el) el.remove(); return; }
  if (!el) { el = document.createElement('script'); el.type = 'application/ld+json'; el.id = 'jsonld-page'; document.head.appendChild(el); }
  el.textContent = JSON.stringify(obj);
}
const SEO_ROUTES = {
  home:        { t: 'JT Atelier — Handcrafted Genuine Leather Jackets, Made to Order', d: 'Luxury genuine-leather jackets, handmade to order by master artisans and shipped worldwide. Aviator, biker, blazer, bomber and fully bespoke leather outerwear.' },
  collection:  { t: "Leather Jacket Collection — Men's & Women's | JT Atelier", d: 'Browse the full JT Atelier collection of genuine-leather jackets — aviator, biker, blazer, bomber, racer and more. Each piece made to order and shipped worldwide.' },
  journal:     { t: 'The Journal — Leather Care, Style & Craft | JT Atelier', d: 'Editorial notes on leather care, jacket styling and the craft behind every JT Atelier piece.' },
  faq:         { t: 'Frequently Asked Questions | JT Atelier', d: 'Answers on sizing, leather types, shipping, custom orders, care and returns for JT Atelier handcrafted leather jackets.' },
  returns:     { t: 'Returns & Refunds Policy | JT Atelier', d: 'Every JT Atelier jacket is made to order. We accept returns only for a verified manufacturing flaw — reported within 2 days with photos.' },
  sizing:      { t: 'Size Guide — Find Your Fit | JT Atelier', d: 'Measure your chest, shoulders and arms and find your perfect JT Atelier leather jacket size, or order bespoke for an exact fit.' },
  about:       { t: 'About JT Atelier — Our Leather Craft', d: 'JT Atelier handcrafts genuine-leather jackets to order, made by skilled artisans in Sialkot and shipped worldwide.' },
  contact:     { t: 'Contact Us | JT Atelier', d: 'Questions about an order, sizing or a bespoke commission? Reach the JT Atelier team — we reply within 24 hours.' },
  'affiliate-apply': { t: 'Affiliate Program | JT Atelier', d: 'Partner with JT Atelier and earn on every referral. Apply to the affiliate program.' },
  privacy:  { t: 'Privacy Policy | JT Atelier', d: 'How JT Atelier collects, uses and protects your personal data in accordance with GDPR and applicable law.' },
  shipping: { t: 'Shipping Policy | JT Atelier', d: 'Free worldwide shipping on all JT Atelier made-to-order leather jackets. Delivery in 2–4 weeks after production.' },
  terms:    { t: 'Terms & Conditions | JT Atelier', d: 'Terms and conditions for purchasing from JT Atelier — made-to-order leather jackets shipped worldwide.' },
};
const NOINDEX_SCREENS = { account: 1, 'account-verify': 1, 'affiliate-portal': 1, confirmation: 1, survey: 1 };
const FAQ_JSONLD = {
  "@context": "https://schema.org", "@type": "FAQPage",
  "mainEntity": [
    { "@type": "Question", "name": "What kind of leather do you use?", "acceptedAnswer": { "@type": "Answer", "text": "Premium genuine cowhide, lambskin and shearling — cowhide for durability, lambskin for softness, shearling for warmth. Every jacket is 100% real leather, never faux." } },
    { "@type": "Question", "name": "How long does shipping take?", "acceptedAnswer": { "@type": "Answer", "text": "Two to four weeks. Because every jacket is made to order, we take the time to handcraft a piece you can pass down for generations. We ship worldwide." } },
    { "@type": "Question", "name": "How do I know what size to order?", "acceptedAnswer": { "@type": "Answer", "text": "Measure your chest, shoulders and arms and check our detailed size guide, or order bespoke for an exact, made-to-measure fit." } },
    { "@type": "Question", "name": "What is your return policy?", "acceptedAnswer": { "@type": "Answer", "text": "Every jacket is made to order, so all sales are final except for a genuine manufacturing flaw. Report a defect within 2 days of delivery with photos and we will repair, replace or refund it and cover return shipping." } },
    { "@type": "Question", "name": "Can I get a jacket customized?", "acceptedAnswer": { "@type": "Answer", "text": "Yes. Start a bespoke commission from the Bespoke section to customize leather, lining, colour, hardware and measurements." } },
    { "@type": "Question", "name": "Where are your jackets made?", "acceptedAnswer": { "@type": "Answer", "text": "Handcrafted in Sialkot, Pakistan — home to some of the world's finest leather manufacturing — by highly skilled artisans." } }
  ]
};
function updateSEO(screen, product){
  const origin = window.location.origin;
  const path = window.location.pathname + window.location.search;
  const defImg = origin + '/assets/products/men/biker/gustave/image_1.jpg';
  let title, desc, image = defImg, jsonld = null, ogType = 'website';

  if (screen === 'product' && product) {
    const cat = product.category || 'Leather';
    title = product.name + ' — ' + cat + ' Leather Jacket | JT Atelier';
    const raw = (product.desc || '').replace(/\s+/g, ' ').trim();
    desc = raw
      ? raw.slice(0, 155)
      : ('Handcrafted genuine-leather ' + cat.toLowerCase() + ' jacket by JT Atelier, made to order' + (product.colors && product.colors.length ? ' in ' + product.colors.join(', ').toLowerCase() : '') + '. Shipped worldwide.');
    if (product.images && product.images[0]) {
      const im = product.images[0];
      image = im.indexOf('http') === 0 ? im : origin + '/' + im.replace(/^\//, '');
    }
    ogType = 'product';
    jsonld = {
      "@context": "https://schema.org", "@type": "Product",
      "name": product.name, "category": cat, "image": image, "description": desc,
      "brand": { "@type": "Brand", "name": "JT Atelier" },
      "material": product.materials || "Genuine leather",
      "offers": { "@type": "Offer", "priceCurrency": "USD", "price": String(product.price), "availability": "https://schema.org/MadeToOrder", "url": origin + path }
    };
  } else {
    const r = SEO_ROUTES[screen] || SEO_ROUTES.home;
    title = r.t; desc = r.d;
    if (screen === 'faq') jsonld = FAQ_JSONLD;
  }

  document.title = title;
  _seoMetaName('description', desc);
  _seoMetaName('robots', NOINDEX_SCREENS[screen] ? 'noindex, follow' : 'index, follow, max-image-preview:large');
  _seoCanonical(origin + path);
  _seoMetaProp('og:type', ogType);
  _seoMetaProp('og:title', title);
  _seoMetaProp('og:description', desc);
  _seoMetaProp('og:url', origin + path);
  _seoMetaProp('og:image', image);
  _seoMetaName('twitter:title', title);
  _seoMetaName('twitter:description', desc);
  _seoMetaName('twitter:image', image);
  _seoPageJsonLd(jsonld);
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [saleEpoch, setSaleEpoch] = React.useState(0);
  // Horizontal home deck is the live home. ?deck=off restores the classic home.
  const deckOn = React.useMemo(() => {
    try { return new URLSearchParams(location.search).get('deck') !== 'off' && window.DECK_ON !== false; }
    catch (e) { return true; }
  }, []);

  // Load dynamic sale config from admin panel — updates window.SALE_CONFIG
  // then bumps saleEpoch so all product cards re-render with the new prices.
  React.useEffect(() => {
    fetch('/api/discounts?action=sale-config')
      .then(r => r.ok ? r.json() : null)
      .then(d => {
        if (d && d.config) {
          window.SALE_CONFIG = Object.assign({}, window.SALE_CONFIG || {}, d.config);
        }
        setSaleEpoch(e => e + 1);
      })
      .catch(() => {});
  }, []);

  // Derive initial screen from URL
  const [screen, setScreen] = React.useState(() => parseRoute().screen);
  const [collectionInit, setCollectionInit] = React.useState(() => {
    const r = parseRoute();
    return { gender: r.gender || null, category: r.category || null, season: r.season || null };
  });
  const [selectedProduct, setSelectedProduct] = React.useState(null);
  const [collectionSlug, setCollectionSlug] = React.useState(() => parseRoute().collectionSlug || null);

  const [cart, setCart] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem('jt_cart') || '[]'); } catch (e) { return []; }
  });
  const [orderRef, setOrderRef] = React.useState(null);
  const [cartOpen, setCartOpen] = React.useState(false);
  const [searchOpen, setSearchOpen] = React.useState(false);
  const [nlOpen, setNlOpen] = React.useState(false);
  const [commissionOpen, setCommissionOpen] = React.useState(false);
  const [bespokeOpen, setBespokeOpen] = React.useState(false);
  const [bespokeInit, setBespokeInit] = React.useState({ product: null, category: null });

  // Resolve product from URL on initial load (after products load)
  React.useEffect(() => {
    const r = parseRoute();
    if (r.screen === 'product' && r.productId) {
      const tryResolve = () => {
        const p = (window.PRODUCTS || []).find(p => p.id === r.productId);
        if (p) { setSelectedProduct(p); setScreen('product'); }
      };
      tryResolve();
      window.addEventListener('products-updated', tryResolve, { once: true });
    }
  }, []);

  // Persist cart
  React.useEffect(() => {
    try { localStorage.setItem('jt_cart', JSON.stringify(cart)); } catch (e) {}
  }, [cart]);

  // Per-route SEO: update <title>, meta, canonical, Open Graph & JSON-LD
  React.useEffect(() => {
    try { updateSEO(screen, selectedProduct); } catch (e) {}
  }, [screen, selectedProduct]);

  // Affiliate ref code detection — runs once on page load
  React.useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const ref = params.get('ref');
    if (ref && /^[A-Za-z0-9_-]{2,50}$/.test(ref)) {
      const code = ref.toUpperCase();
      try {
        localStorage.setItem('jta_ref', code);
        localStorage.setItem('jta_ref_time', String(Date.now()));
      } catch (e) {}
      // Fire click tracking silently — don't block the page
      fetch('/api/orders?a=aff-click', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code }),
      }).catch(() => {});
      // Clean the ref param from the URL so it doesn't persist visually
      const clean = new URL(window.location.href);
      clean.searchParams.delete('ref');
      window.history.replaceState({}, '', clean.toString());
    }
  }, []);

  // Handle the return from Stripe Checkout (success or cancel).
  React.useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const co = params.get('checkout');
    if (co === 'success') {
      const ref = params.get('ref') || '';
      setOrderRef(ref);

      // Fallback order recording — if the Stripe webhook didn't fire (mis-config,
      // wrong mode, etc.), reconcile the order from the session so it still lands
      // in admin + the emails go out. Idempotent + webhook-deferring server-side.
      const sessionId = params.get('session_id') || '';
      if (sessionId) {
        fetch('/api/confirm-order', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ session_id: sessionId }),
        }).catch(() => {});
      }

      // GA4 purchase event — fired once per completed order
      try {
        if (typeof gtag === 'function' && cart.length) {
          const currency = (window.LO_CURRENCY && window.LO_CURRENCY.code) || 'USD';
          const rate     = (window.LO_CURRENCY && window.LO_CURRENCY.rate) || 1;
          const catalog  = new Map((window.PRODUCTS || []).map(p => [p.id, p]));
          let value = 0;
          const gaItems = cart.map(item => {
            const product = catalog.get(item.id) || {};
            const base    = Number(product.price || 0);
            const sale    = window.SALE_CONFIG;
            let pct = 0;
            if (sale) {
              if (sale.sitewide > 0) pct = sale.sitewide;
              if (sale.categories && sale.categories[product.category] > 0) pct = sale.categories[product.category];
              if (sale.items && sale.items[product.id] > 0) pct = sale.items[product.id];
            }
            const unitUSD = pct > 0 ? Math.round(base * (1 - pct / 100)) : base;
            const price   = Math.round(unitUSD * rate * 100) / 100;
            value += price * (item.qty || 1);
            return {
              item_id:   item.id,
              item_name: product.name || product.frenchName || item.id,
              quantity:  item.qty || 1,
              price,
            };
          });
          gtag('event', 'purchase', {
            transaction_id: ref,
            currency,
            value: Math.round(value * 100) / 100,
            items: gaItems,
          });
        }
      } catch (e) {}

      setCart([]);
      // Clear affiliate attribution after a conversion — subsequent purchases won't be credited
      try { localStorage.removeItem('jta_ref'); localStorage.removeItem('jta_ref_time'); } catch (e) {}
      setScreen('confirmation');
      window.history.replaceState({}, '', '/');
    } else if (co === 'cancel') {
      window.history.replaceState({}, '', window.location.pathname);
    }
  }, []);

  // Browser back/forward
  React.useEffect(() => {
    const onPop = () => {
      const r = parseRoute();
      setScreen(r.screen);
      if (r.screen === 'collection') setCollectionInit({ gender: r.gender, category: r.category, season: r.season });
      if (r.screen === 'product' && r.productId) {
        const p = (window.PRODUCTS || []).find(p => p.id === r.productId);
        if (p) setSelectedProduct(p);
      }
      if (r.screen === 'collections') setCollectionSlug(r.collectionSlug || null);
      window.scrollTo({ top: 0, behavior: 'instant' });
    };
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  // Apply CSS variables for tweaks
  React.useEffect(() => {
    const r = document.documentElement.style;
    r.setProperty('--font-display', FONT_PAIRS[t.fontPair].display);
    r.setProperty('--font-body',    FONT_PAIRS[t.fontPair].body);
    r.setProperty('--grain', String(t.grain));
    document.body.classList.toggle('light', !t.dark);
  }, [t.fontPair, t.grain, t.dark]);

  // Expose globals
  React.useEffect(() => {
    window.openCommission = () => setCommissionOpen(true);
    window.openBespoke = (opts = {}) => {
      setBespokeInit({ product: opts.product || null, category: opts.category || null });
      setBespokeOpen(true);
    };
    return () => { delete window.openCommission; delete window.openBespoke; };
  }, []);

  // Esc closes overlays
  React.useEffect(() => {
    const k = (e) => {
      if (e.key === 'Escape') {
        setCartOpen(false); setSearchOpen(false); setNlOpen(false);
        setCommissionOpen(false); setBespokeOpen(false);
      }
    };
    window.addEventListener('keydown', k);
    return () => window.removeEventListener('keydown', k);
  }, []);

  const goTo = (s, opts = {}) => {
    if (s === 'collection') setCollectionInit({
      gender:   opts.gender   || null,
      category: opts.category || null,
      season:   opts.season   || null,
    });
    if (s === 'product') setSelectedProduct(opts.product || null);
    if (s === 'collections') setCollectionSlug(opts.collectionSlug || null);
    setScreen(s);
    const url = buildUrl(s, opts);
    window.history.pushState({}, '', url);
    window.scrollTo({ top: 0, behavior: 'instant' });
  };

  const addToCart = (item) => {
    const cartId = `${item.id}-${item.size}-${item.color}-${Date.now()}`;
    setCart((prev) => {
      // Bespoke items are always unique — never merge them
      if (item.bespokeConfig) return [...prev, { ...item, cartId }];
      const existing = prev.find((p) => p.id === item.id && p.size === item.size && p.color === item.color);
      if (existing) {
        return prev.map((p) => (p === existing ? { ...p, qty: p.qty + item.qty } : p));
      }
      return [...prev, { ...item, cartId }];
    });
    setTimeout(() => setCartOpen(true), 200);
  };
  const removeItem = (cartId) => setCart((p) => p.filter((i) => i.cartId !== cartId));
  const updateQty  = (cartId, qty) =>
    setCart((p) => p.map((i) => (i.cartId === cartId ? { ...i, qty } : i)));

  const cartCount = cart.reduce((s, i) => s + i.qty, 0);

  return (
    <div className="shell">

      <AnnouncementBar />
      <Nav screen={screen} goTo={goTo}
        cartCount={cartCount}
        openCart={() => setCartOpen(true)}
        openSearch={() => setSearchOpen(true)} />
      <EditorialChrome goTo={goTo}
        screen={screen}
        cartCount={cartCount}
        openCart={() => setCartOpen(true)}
        openSearch={() => setSearchOpen(true)} />

      {screen === 'home' && (deckOn && window.HomeDeck
        ? <window.HomeDeck goTo={goTo} FooterCmp={window.Footer} openNewsletter={() => setNlOpen(true)} />
        : <Home goTo={goTo} t={t} />)}
      {screen === 'collection' && <Collection goTo={goTo} density={t.density}
        initialGender={collectionInit.gender}
        initialCategory={collectionInit.category}
        initialSeason={collectionInit.season} />}
      {screen === 'product' && selectedProduct && <Product product={selectedProduct} goTo={goTo} addToCart={addToCart} />}
      {screen === 'about' && <About goTo={goTo} />}
      {screen === 'contact' && <Contact goTo={goTo} />}
      {screen === 'affiliate-apply'   && <AffiliateApply goTo={goTo} />}
      {screen === 'affiliate-portal'  && <AffiliatePortal goTo={goTo} />}
      {(screen === 'account' || screen === 'account-verify') && <CustomerAccount goTo={goTo} isVerify={screen === 'account-verify'} />}
      {screen === 'collections' && <CollectionsPage goTo={goTo} slug={collectionSlug} />}
      {screen === 'journal' && <Journal goTo={goTo} />}
      {screen === 'faq'     && <FAQ goTo={goTo} />}
      {screen === 'returns' && <Returns goTo={goTo} />}
      {screen === 'sizing'  && <SizingPage goTo={goTo} openBespoke={() => setBespokeOpen(true)} />}
      {screen === 'confirmation' && <OrderConfirmation orderRef={orderRef} goTo={goTo} />}
      {screen === 'survey'  && <Survey />}
      {screen === 'privacy'  && <PolicyPage type="privacy"  goTo={goTo} />}
      {screen === 'shipping' && <PolicyPage type="shipping" goTo={goTo} />}
      {screen === 'terms'    && <PolicyPage type="terms"    goTo={goTo} />}

      {/* Deck home is horizontal-only — no newsletter band below it (the footer panel carries it). */}
      {!(screen === 'home' && deckOn) && <EmailCapture />}
      {/* Social embeds live on the About page only (moved out of the global layout). */}
      {screen === 'about' && <SocialFeed />}
      {/* Deck supplies its own footer panel; hide the global footer on the deck home. */}
      {!(screen === 'home' && deckOn) && <Footer openNewsletter={() => setNlOpen(true)} goTo={goTo} />}

      <CartDrawer open={cartOpen} onClose={() => setCartOpen(false)}
        items={cart} removeItem={removeItem} updateQty={updateQty} goTo={goTo} />
      <NewsletterModal open={nlOpen} onClose={() => setNlOpen(false)} />
      {typeof WelcomeOffer !== 'undefined' && <WelcomeOffer />}
      <SearchOverlay open={searchOpen} onClose={() => setSearchOpen(false)} goTo={goTo} />
      <CommissionWizard open={commissionOpen} onClose={() => setCommissionOpen(false)} />
      <BespokeScreen open={bespokeOpen} onClose={() => setBespokeOpen(false)}
        initialProduct={bespokeInit.product} initialCategory={bespokeInit.category}
        addToCart={addToCart} />
      <WhatsApp />

      <TweaksPanel title="Tweaks">
        <TweakSection label="Aesthetic" />
        <TweakToggle label="Dark mode" value={t.dark}
          onChange={(v) => setTweak('dark', v)} />
        <TweakRadio label="Type" value={t.fontPair}
          options={[
            { value: 'editorial',  label: 'Bodoni' },
            { value: 'instrument', label: 'Instrument' },
            { value: 'classic',    label: 'Classic' },
            { value: 'brutalist',  label: 'Brutalist' },
          ]}
          onChange={(v) => setTweak('fontPair', v)} />

        <TweakSection label="Layout" />
        <TweakRadio label="Hero" value={t.heroVariant}
          options={[
            { value: 'centered',  label: 'Centered' },
            { value: 'split',     label: 'Split' },
            { value: 'editorial', label: 'Cover' },
          ]}
          onChange={(v) => setTweak('heroVariant', v)} />
        <TweakRadio label="Grid density" value={t.density}
          options={[
            { value: 'compact', label: 'Compact' },
            { value: 'regular', label: 'Regular' },
            { value: 'comfy',   label: 'Comfy' },
          ]}
          onChange={(v) => setTweak('density', v)} />

        <TweakSection label="Atmosphere" />
        <TweakSlider label="Particles" value={t.particles}
          min={0} max={1.5} step={.1}
          onChange={(v) => setTweak('particles', v)} />
        <TweakSlider label="Grain" value={t.grain}
          min={0} max={2} step={.1}
          onChange={(v) => setTweak('grain', v)} />

        <TweakSection label="Quick nav" />
        <div style={{ display: 'flex', gap: 6 }}>
          <TweakButton label="Home"    onClick={() => goTo('home')}       secondary />
          <TweakButton label="Shop"    onClick={() => goTo('collection')} secondary />
          <TweakButton label="About"   onClick={() => goTo('about')}      secondary />
          <TweakButton label="Contact" onClick={() => goTo('contact')}    secondary />
        </div>
      </TweaksPanel>
    </div>
  );
}

function isLight() { return false; }

// Robust mount: the in-browser Babel transformer executes each per-file
// component <script> asynchronously, so on a cold/slow load App can attempt to
// render before an eagerly-used component (CartDrawer, from overlays.jsx) has
// finished loading — which threw "CartDrawer is not defined" and relied on a
// React retry to recover. Wait until CartDrawer exists, with a hard ~3s cap so
// a genuine load failure still falls back to rendering (never worse, never spins).
// typeof on an undeclared global is safe and never throws.
(function () {
  var root = ReactDOM.createRoot(document.getElementById('root'));
  var tries = 0;
  (function mount() {
    if (typeof CartDrawer === 'undefined' && tries++ < 150) {
      return setTimeout(mount, 20);
    }
    root.render(<App />);
  })();
})();
