// ============================================
// WEBSHOP — publiek
// Routes (binnen één component, hash-gebaseerd):
//   #/webshop               → product-grid
//   #/webshop/product/:id   → product-detail
//   #/webshop/cart          → winkelmandje + checkout-vorm
//   #/webshop/return?id=    → bevestigingspagina na Mollie
// ============================================

// ---- Webshop helpers (Fase 1: varianten / galerij / sale) ----
function shopHasVariants(p) { return Array.isArray(p?.variants) && p.variants.length > 0; }
function shopImages(p) {
  const arr = (p?.images && p.images.length) ? p.images : (p?.image ? [p.image] : []);
  return arr.filter(Boolean);
}
function shopPrimaryImage(p) { return shopImages(p)[0] || ''; }
function shopPriceFrom(p) { return Number(p?.priceFrom ?? p?.price) || 0; }
function fmtEur(n) { return '€' + (Number(n) || 0).toFixed(2); }
// Vind de variant die past bij de gekozen opties (alle opties moeten gekozen zijn)
function shopVariantByOptions(p, opts) {
  if (!shopHasVariants(p)) return null;
  const names = (p.variantOptions || []).map(o => o.name);
  if (names.some(n => !opts || !opts[n])) return null;
  return p.variants.find(v => names.every(n => (v.options || {})[n] === opts[n])) || null;
}

// Prijsweergave met optionele "vanaf" en doorstreepte oude prijs (SALE)
function PriceTag({ amount, compareAt, from, style }) {
  const onSale = compareAt != null && Number(compareAt) > Number(amount);
  return (
    <span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 8, ...(style || {}) }}>
      {from && <span style={{ fontSize: '0.62em', color: 'var(--ink-500)', fontWeight: 400 }}>vanaf </span>}
      <span style={{ color: onSale ? 'var(--blue-600, #c0392b)' : 'inherit' }}>{fmtEur(amount)}</span>
      {onSale && <span style={{ fontSize: '0.6em', color: 'var(--ink-400)', textDecoration: 'line-through', fontWeight: 400 }}>{fmtEur(compareAt)}</span>}
    </span>
  );
}

// Sterrenbeoordeling — weergave of interactief (kies een score)
function StarRating({ value = 0, size = 16, interactive = false, onChange }) {
  return (
    <span style={{ display: 'inline-flex', gap: 1 }} role={interactive ? 'radiogroup' : undefined}>
      {[1, 2, 3, 4, 5].map(s => {
        const filled = Number(value) >= s - 0.25;
        return (
          <span key={s}
            onClick={interactive && onChange ? () => onChange(s) : undefined}
            title={interactive ? s + ' / 5' : undefined}
            style={{
              cursor: interactive ? 'pointer' : 'default',
              color: filled ? '#f5a623' : 'var(--ink-200)',
              fontSize: size, lineHeight: 1, userSelect: 'none',
            }}>★</span>
        );
      })}
    </span>
  );
}

// Compacte rating-samenvatting: sterren + "4.3 (12)"
function RatingSummary({ rating, size = 14, onClick }) {
  if (!rating || !rating.count) return null;
  return (
    <span onClick={onClick} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, cursor: onClick ? 'pointer' : 'default' }}>
      <StarRating value={rating.avg} size={size} />
      <span style={{ fontSize: size - 1, color: 'var(--ink-500)' }}>{rating.avg.toFixed(1)} ({rating.count})</span>
    </span>
  );
}

// Hartje voor verlanglijst (op kaart of detail).
// Onzichtbaar als 'shop-wishlist' niet in het huidige WNM-HOST pakket zit.
function WishlistHeart({ active, onClick, size = 20, floating = false }) {
  if (window.WNM && !window.WNM.hasFeature('shop-wishlist')) return null;
  return (
    <button onClick={onClick} aria-label={active ? 'Verwijder uit verlanglijst' : 'Voeg toe aan verlanglijst'} title={active ? 'In verlanglijst' : 'Aan verlanglijst toevoegen'}
      style={{
        background: floating ? 'rgba(22,22,28,0.92)' : 'transparent',
        border: floating ? '1px solid var(--ink-100)' : 'none',
        borderRadius: 999, width: floating ? 38 : 'auto', height: floating ? 38 : 'auto',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        cursor: 'pointer', padding: floating ? 0 : 4, lineHeight: 1,
        color: active ? '#e23' : 'var(--ink-400)', fontSize: size,
        boxShadow: floating ? '0 2px 8px rgba(14,26,38,.12)' : 'none',
      }}>
      {active ? '♥' : '♡'}
    </button>
  );
}

function WebshopPage({ navigate }) {
  const { Placeholder, Reveal, SectionHeader, Icon } = window.LM_UI;
  const { t } = window.LM_CMS;

  const getSubPath = () => {
    const h = (window.location.hash || '').replace(/^#\//, '').split('?')[0];
    return h;
  };

  const [sub, setSub] = React.useState(getSubPath);
  const [cartCount, setCartCount] = React.useState(window.LM_SHOP.cart.count());
  const [me, setMe] = React.useState(null); // {authed, customer}
  const [wishlistIds, setWishlistIds] = React.useState([]);
  const [giftEnabled, setGiftEnabled] = React.useState(true); // master-schakelaar (admin)

  const applyMe = (m) => {
    setMe(m);
    setWishlistIds((m && m.customer && Array.isArray(m.customer.wishlist)) ? m.customer.wishlist : []);
    return m;
  };

  React.useEffect(() => {
    const onHash = () => { setSub(getSubPath()); window.scrollTo(0, 0); };
    const onCart = () => setCartCount(window.LM_SHOP.cart.count());
    window.addEventListener('hashchange', onHash);
    window.addEventListener('lm-cart-changed', onCart);
    fetch('/api/shop/account/me').then(r => r.json()).then(applyMe).catch(() => applyMe({ authed: false }));
    // Cadeaubon master-schakelaar ophalen (uit te zetten in admin)
    window.LM_SHOP.getConfig().then(cfg => setGiftEnabled(cfg?.giftCards?.enabled !== false)).catch(() => {});
    return () => {
      window.removeEventListener('hashchange', onHash);
      window.removeEventListener('lm-cart-changed', onCart);
    };
  }, []);

  const refreshMe = () => fetch('/api/shop/account/me').then(r => r.json()).then(applyMe);

  // Verlanglijst togglen — vereist login
  const toggleWishlist = async (productId) => {
    if (!me?.authed) { navigate('webshop/login'); return; }
    const has = wishlistIds.includes(productId);
    // optimistisch
    setWishlistIds(prev => has ? prev.filter(id => id !== productId) : [productId, ...prev]);
    try {
      const res = has ? await window.LM_SHOP.removeWishlist(productId) : await window.LM_SHOP.addWishlist(productId);
      setWishlistIds(res.ids || []);
    } catch {
      setWishlistIds(prev => has ? [productId, ...prev] : prev.filter(id => id !== productId)); // rollback
    }
  };

  const wl = { ids: wishlistIds, toggle: toggleWishlist, authed: !!me?.authed };

  let body;
  if (sub.startsWith('webshop/product/')) {
    body = <ProductDetail id={sub.replace('webshop/product/', '')} navigate={navigate} wl={wl} />;
  } else if (sub === 'webshop/cart') {
    body = <CartPage navigate={navigate} me={me} onLogin={refreshMe} />;
  } else if (sub === 'webshop/return') {
    body = <OrderReturnPage navigate={navigate} />;
  } else if (sub === 'webshop/cadeaubon') {
    body = giftEnabled
      ? <GiftCardPage navigate={navigate} />
      : <GiftCardsDisabledPage navigate={navigate} />;
  } else if (sub === 'webshop/gift-return') {
    body = <GiftReturnPage navigate={navigate} />;
  } else if (sub === 'webshop/wishlist') {
    body = <WishlistPage navigate={navigate} me={me} wl={wl} />;
  } else if (sub === 'webshop/account') {
    body = <AccountPage navigate={navigate} me={me} onChange={refreshMe} />;
  } else if (sub === 'webshop/login') {
    body = <LoginPage navigate={navigate} onSuccess={() => { refreshMe(); navigate('webshop/cart'); }} />;
  } else if (sub === 'webshop/register') {
    body = <RegisterPage navigate={navigate} onSuccess={() => { refreshMe(); navigate('webshop/cart'); }} />;
  } else if (sub === 'webshop/forgot-password') {
    body = <ForgotPasswordPage navigate={navigate} />;
  } else if (sub === 'webshop/set-password') {
    body = <SetPasswordPage navigate={navigate} onSuccess={() => { refreshMe(); navigate('webshop/account'); }} />;
  } else {
    body = <ProductGrid navigate={navigate} wl={wl} />;
  }

  return (
    <div>
      <ShopHeader navigate={navigate} cartCount={cartCount} me={me} wishlistCount={wishlistIds.length} giftEnabled={giftEnabled} />
      {body}
    </div>
  );
}

// ============================================
// Shop sub-header — alleen zichtbaar binnen /webshop routes
// Toont winkelmandje-knop met count + account/login link
// ============================================
function ShopHeader({ navigate, cartCount, me, wishlistCount = 0, giftEnabled = true }) {
  return (
    <div style={{
      background: 'var(--surface)',
      borderBottom: '1px solid var(--border)',
      padding: '12px 0',
      position: 'sticky',
      top: 0,
      zIndex: 50,
      boxShadow: '0 1px 0 rgba(14,26,38,.04)',
    }}>
      <div className="container" style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
        <button onClick={() => navigate('webshop')} style={{
          background: 'none', border: 'none', cursor: 'pointer',
          fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 18,
          textTransform: 'uppercase', letterSpacing: '0.02em',
          color: 'var(--ink-900)',
        }}>
          <span className="grad-text">SHOP</span>
        </button>
        <div style={{ flex: 1 }} />
        <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
          {me?.authed ? (
            <button onClick={() => navigate('webshop/account')} style={{
              background: 'var(--ink-50)', border: 'none', borderRadius: 999,
              padding: '8px 16px', cursor: 'pointer',
              fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600,
              display: 'flex', alignItems: 'center', gap: 8,
            }}>
              <span style={{ width: 22, height: 22, borderRadius: '50%', background: 'var(--gradient)', color: 'white', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 700 }}>
                {(me.customer?.name || '?').split(' ').map(p => p[0]).join('').slice(0, 2).toUpperCase()}
              </span>
              <span style={{ maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{me.customer?.name}</span>
            </button>
          ) : (
            <button onClick={() => navigate('webshop/login')} style={{
              background: 'transparent', border: '1.5px solid var(--ink-200)',
              padding: '7px 14px', borderRadius: 999, cursor: 'pointer',
              fontFamily: 'var(--font-body)', fontWeight: 600, fontSize: 13,
              color: 'var(--ink-700)',
            }}>
              Aanmelden
            </button>
          )}

          {giftEnabled && (!window.WNM || window.WNM.hasFeature('giftcards')) && (
            <button onClick={() => navigate('webshop/cadeaubon')} title="Cadeaubon kopen" style={{
              background: 'transparent', border: '1.5px solid var(--ink-200)',
              padding: '7px 12px', borderRadius: 999, cursor: 'pointer',
              fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600,
              color: 'var(--ink-700)', display: 'flex', alignItems: 'center', gap: 6,
            }}>
              <span>🎁</span><span className="lm-hide-sm">Cadeaubon</span>
            </button>
          )}

          {(!window.WNM || window.WNM.hasFeature('shop-wishlist')) && (
            <button onClick={() => navigate('webshop/wishlist')} title="Verlanglijst" aria-label="Verlanglijst" style={{
              background: 'transparent', border: '1.5px solid var(--ink-200)',
              padding: '7px 12px', borderRadius: 999, cursor: 'pointer',
              fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600,
              color: 'var(--ink-700)', display: 'flex', alignItems: 'center', gap: 6,
              position: 'relative',
            }}>
              <span style={{ color: '#e23', fontSize: 15 }}>♥</span>
              {wishlistCount > 0 && (
                <span style={{
                  background: '#e23', color: 'white', fontWeight: 800, fontSize: 11,
                  minWidth: 20, height: 20, borderRadius: '50%',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: '0 5px',
                }}>{wishlistCount}</span>
              )}
            </button>
          )}

          <button onClick={() => navigate('webshop/cart')} style={{
            background: 'var(--gradient)', color: 'white', border: 'none',
            padding: '8px 14px', borderRadius: 999, cursor: 'pointer',
            fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600,
            display: 'flex', alignItems: 'center', gap: 8,
            position: 'relative',
          }}>
            <span>🛒</span>
            <span>Mandje</span>
            {cartCount > 0 && (
              <span style={{
                background: 'var(--teal-500)', color: 'var(--ink-900)',
                fontWeight: 800, fontSize: 11,
                minWidth: 22, height: 22, borderRadius: '50%',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                padding: '0 6px',
              }}>{cartCount}</span>
            )}
          </button>
        </div>
      </div>
    </div>
  );
}

// ============================================
// Product-grid
// ============================================
function ProductGrid({ navigate, wl }) {
  const { Placeholder, Reveal, SectionHeader, Icon } = window.LM_UI;
  const { t } = window.LM_CMS;
  const [products, setProducts] = React.useState(null);
  const [category, setCategory] = React.useState('all');
  const [q, setQ] = React.useState('');
  const [sort, setSort] = React.useState('featured');
  const [inStockOnly, setInStockOnly] = React.useState(false);
  const [saleOnly, setSaleOnly] = React.useState(false);
  const [kidsOnly, setKidsOnly] = React.useState(false);
  const [openCats, setOpenCats] = React.useState({}); // welke categorie-secties zijn opengeklapt
  const toggleCat = (name) => setOpenCats(s => ({ ...s, [name]: !s[name] }));

  React.useEffect(() => {
    window.LM_SHOP.listProducts().then(setProducts).catch(() => setProducts([]));
  }, []);

  if (products === null) return <div style={{ padding: 80, textAlign: 'center', color: 'var(--ink-500)' }}>Producten laden…</div>;

  const cats = ['all', ...Array.from(new Set(products.map(p => p.category).filter(Boolean)))];
  const isSale = (p) => p.compareAtPrice != null && Number(p.compareAtPrice) > shopPriceFrom(p);
  const norm = (s) => (s || '').toString().toLowerCase();
  const term = norm(q.trim());

  let filtered = products.filter(p => {
    if (category !== 'all' && p.category !== category) return false;
    if (inStockOnly && p.inStock === false) return false;
    if (saleOnly && !isSale(p)) return false;
    if (kidsOnly && !p.forKids) return false;
    if (term) {
      const hay = [p.name, p.description, p.sku, p.category].map(norm).join(' ');
      if (!hay.includes(term)) return false;
    }
    return true;
  });
  const byDate = (a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0);
  filtered = [...filtered].sort((a, b) => {
    if (sort === 'price_asc') return shopPriceFrom(a) - shopPriceFrom(b);
    if (sort === 'price_desc') return shopPriceFrom(b) - shopPriceFrom(a);
    if (sort === 'name') return norm(a.name).localeCompare(norm(b.name));
    if (sort === 'newest') return byDate(a, b);
    const f = (b.featured ? 1 : 0) - (a.featured ? 1 : 0);
    return f !== 0 ? f : byDate(a, b);
  });

  // Gegroepeerde weergave: bij 'Alle producten' zonder zoekterm tonen we de producten
  // onder categorie-koppen, zodat klanten makkelijk per categorie kunnen bladeren.
  const showGrouped = category === 'all' && !term;
  const grouped = (() => {
    if (!showGrouped) return [];
    const order = cats.filter(c => c !== 'all');
    const seen = new Set();
    const groups = [];
    for (const name of order) {
      const items = filtered.filter(p => p.category === name);
      if (items.length) { groups.push({ name, items }); seen.add(name); }
    }
    const rest = filtered.filter(p => !seen.has(p.category));
    if (rest.length) groups.push({ name: 'Overige', items: rest });
    return groups;
  })();

  const inputStyle = { padding: '10px 14px', borderRadius: 10, border: '1.5px solid var(--ink-200)', fontSize: 14, fontFamily: 'inherit', background: 'var(--surface)' };
  const hasFilters = !!term || category !== 'all' || inStockOnly || saleOnly || kidsOnly;

  return (
    <div>
      <section className="section" style={{ paddingBottom: 24 }}>
        <div className="container">
          <SectionHeader
            eyebrow={t('webshop.intro.eyebrow', 'Webshop')}
            title={<>{t('webshop.intro.title1', "Dance Studio")} <span className="grad-text">{t('webshop.intro.title2', 'merchandise')}</span></>}
            subtitle={t('webshop.intro.subtitle', 'Onze eigen kleding en accessoires. Bestel online, betaal veilig via Mollie en haal af in de winkel.')}
          />

          <Reveal>
            {/* Zoeken + sorteren */}
            <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'center', marginBottom: 16 }}>
              <div style={{ position: 'relative', flex: '1 1 260px' }}>
                <span style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', opacity: 0.45, pointerEvents: 'none' }}>
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" /></svg>
                </span>
                <input value={q} onChange={e => setQ(e.target.value)} placeholder="Zoek een product…" style={{ ...inputStyle, width: '100%', paddingLeft: 38 }} />
              </div>
              <select value={sort} onChange={e => setSort(e.target.value)} style={inputStyle}>
                <option value="featured">Aanbevolen</option>
                <option value="newest">Nieuwste eerst</option>
                <option value="price_asc">Prijs: laag → hoog</option>
                <option value="price_desc">Prijs: hoog → laag</option>
                <option value="name">Naam: A → Z</option>
              </select>
            </div>

            {/* Categorie + filters */}
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center', marginBottom: 10 }}>
              {cats.map(c => (
                <button key={c} className={`chip ${category === c ? 'active' : ''}`} onClick={() => setCategory(c)}>
                  {c === 'all' ? 'Alle producten' : c}
                </button>
              ))}
              <span style={{ flex: 1, minWidth: 12 }} />
              <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'var(--ink-700)', cursor: 'pointer' }}>
                <input type="checkbox" checked={inStockOnly} onChange={e => setInStockOnly(e.target.checked)} /> Op voorraad
              </label>
              <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'var(--ink-700)', cursor: 'pointer' }}>
                <input type="checkbox" checked={saleOnly} onChange={e => setSaleOnly(e.target.checked)} /> Aanbiedingen
              </label>
              <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'var(--ink-700)', cursor: 'pointer' }}>
                <input type="checkbox" checked={kidsOnly} onChange={e => setKidsOnly(e.target.checked)} /> 👶 Voor kinderen
              </label>
            </div>
            <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>
              {filtered.length} product{filtered.length === 1 ? '' : 'en'}
              {showGrouped && grouped.length > 1 ? ' · klik op een categorie om ze te openen' : ''}
            </div>
          </Reveal>
        </div>
      </section>

      <section style={{ paddingBottom: 96 }}>
        <div className="container">
          {filtered.length === 0 ? (
            <div style={{ textAlign: 'center', padding: 80, color: 'var(--ink-500)' }}>
              {hasFilters ? 'Geen producten gevonden met deze filters.' : 'Geen producten beschikbaar.'}
              {hasFilters && (
                <div style={{ marginTop: 16 }}>
                  <button className="btn btn-ghost btn-sm" onClick={() => { setQ(''); setCategory('all'); setInStockOnly(false); setSaleOnly(false); setKidsOnly(false); }}>Filters wissen</button>
                </div>
              )}
            </div>
          ) : showGrouped ? (
            grouped.map(group => {
              // Standaard ingeklapt: klant opent zelf de gewenste categorie.
              // Bij één enkele categorie staat ze meteen open (anders oogt de winkel leeg).
              const isOpen = grouped.length === 1 ? true : !!openCats[group.name];
              return (
                <div key={group.name} style={{ marginBottom: 14 }}>
                  <button type="button" onClick={() => toggleCat(group.name)} aria-expanded={isOpen}
                    style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%', background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: 14, padding: '16px 20px', cursor: 'pointer', textAlign: 'left', boxShadow: 'var(--shadow-sm)' }}>
                    <h2 className="display" style={{ fontSize: 22, margin: 0, textTransform: 'none', letterSpacing: '-0.01em' }}>{group.name}</h2>
                    <span className="mono" style={{ fontSize: 12, color: 'var(--ink-500)' }}>{group.items.length} product{group.items.length === 1 ? '' : 'en'}</span>
                    <span style={{ flex: 1 }} />
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ color: 'var(--ink-500)', flexShrink: 0, transform: isOpen ? 'rotate(180deg)' : 'none', transition: 'transform .2s' }}><polyline points="6 9 12 15 18 9" /></svg>
                  </button>
                  {isOpen && (
                    <div className="grid grid-3" style={{ marginTop: 18 }}>
                      {group.items.map((p, i) => (
                        <Reveal key={p.id} delay={(i % 6) * 70}>
                          <ProductCard product={p} onOpen={() => navigate('webshop/product/' + p.id)} wl={wl} />
                        </Reveal>
                      ))}
                    </div>
                  )}
                </div>
              );
            })
          ) : (
            <div className="grid grid-3">
              {filtered.map((p, i) => (
                <Reveal key={p.id} delay={(i % 6) * 70}>
                  <ProductCard product={p} onOpen={() => navigate('webshop/product/' + p.id)} wl={wl} />
                </Reveal>
              ))}
            </div>
          )}
        </div>
      </section>

      <RecentlyViewed navigate={navigate} products={products} wl={wl} />
    </div>
  );
}

// Recent bekeken — lokaal in de browser, exclusief optionele 'excludeId'
function RecentlyViewed({ navigate, products, wl, excludeId, title = 'Recent bekeken' }) {
  const ids = window.LM_SHOP.recentlyViewed.ids().filter(id => id !== excludeId);
  const byId = {};
  (products || []).forEach(p => { byId[p.id] = p; });
  const list = ids.map(id => byId[id]).filter(Boolean).slice(0, 4);
  if (list.length === 0) return null;
  const { Reveal } = window.LM_UI;
  return (
    <section style={{ paddingBottom: 80 }}>
      <div className="container">
        <Reveal>
          <h2 className="display" style={{ fontSize: 24, marginBottom: 20, textTransform: 'none', letterSpacing: '-0.01em' }}>{title}</h2>
          <div className="grid grid-4">
            {list.map(p => (
              <ProductCard key={p.id} product={p} onOpen={() => navigate('webshop/product/' + p.id)} wl={wl} />
            ))}
          </div>
        </Reveal>
      </div>
    </section>
  );
}

function ProductCard({ product, onOpen, wl }) {
  const { Placeholder, Icon } = window.LM_UI;
  const inStock = product.inStock !== false;
  const hasVariants = shopHasVariants(product);
  const priceFrom = shopPriceFrom(product);
  const onSale = product.compareAtPrice != null && Number(product.compareAtPrice) > priceFrom;
  const inWishlist = !!(wl && wl.ids && wl.ids.includes(product.id));

  const addToCart = (e) => {
    e.stopPropagation();
    if (!inStock) return;
    if (hasVariants) { onOpen(); return; } // varianten → kies opties op de detailpagina
    window.LM_SHOP.cart.add(product.id, 1);
  };

  return (
    <article onClick={onOpen} className="card" style={{
      overflow: 'hidden', cursor: 'pointer', height: '100%', display: 'flex', flexDirection: 'column',
      transition: 'transform .2s, box-shadow .2s',
      position: 'relative',
    }}
      onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = 'var(--shadow-lg)'; }}
      onMouseLeave={e => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = 'var(--shadow-sm)'; }}
    >
      <div style={{ position: 'relative' }}>
        <Placeholder label={product.name} src={shopPrimaryImage(product)} ratio="4/5" fit="contain" bg="#fff" style={{ borderRadius: 0, padding: 18 }} />
        {!inStock && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            background: 'var(--gradient)', color: 'white',
            padding: '4px 10px', borderRadius: 4,
            fontSize: 10, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase',
          }}>Uitverkocht</div>
        )}
        {onSale && inStock && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            background: '#c0392b', color: 'white',
            padding: '4px 10px', borderRadius: 4,
            fontSize: 10, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase',
          }}>SALE</div>
        )}
        {product.featured && inStock && !onSale && (
          <div style={{
            position: 'absolute', top: 12, left: 12,
            background: 'var(--gradient)', color: 'white',
            padding: '4px 10px', borderRadius: 4,
            fontSize: 10, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase',
          }}>★ Uitgelicht</div>
        )}
        {product.newCollection && (
          <div style={{
            position: 'absolute',
            top: (!inStock || (onSale && inStock) || (product.featured && inStock && !onSale)) ? 42 : 12,
            left: 12,
            background: 'var(--teal-500, #14b8a6)', color: 'white',
            padding: '4px 10px', borderRadius: 4,
            fontSize: 10, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase',
          }}>✨ Nieuw</div>
        )}
        {wl && (
          <div style={{ position: 'absolute', top: 10, right: 10 }} onClick={e => e.stopPropagation()}>
            <WishlistHeart active={inWishlist} floating onClick={() => wl.toggle(product.id)} />
          </div>
        )}
      </div>
      <div style={{ padding: 20, flex: 1, display: 'flex', flexDirection: 'column' }}>
        {product.category && (
          <div className="mono" style={{ fontSize: 10, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--ink-500)', marginBottom: 6 }}>
            {product.category}
          </div>
        )}
        <h3 className="display" style={{ fontSize: 18, margin: '0 0 8px', textTransform: 'none', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
          {product.name}
        </h3>
        {product.rating && product.rating.count > 0 && (
          <div style={{ marginBottom: 8 }}><RatingSummary rating={product.rating} size={13} /></div>
        )}
        {product.description && (
          <p style={{ fontSize: 13, color: 'var(--ink-500)', margin: '0 0 14px', lineHeight: 1.5, flex: 1 }}>
            {product.description.length > 100 ? product.description.slice(0, 100) + '…' : product.description}
          </p>
        )}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 'auto' }}>
          <div className="display" style={{ fontSize: 22, color: 'var(--ink-900)' }}>
            <PriceTag amount={priceFrom} compareAt={product.compareAtPrice} from={hasVariants} />
          </div>
          {inStock ? (
            <button className="btn btn-sm btn-primary" onClick={addToCart}>{hasVariants ? 'Kies opties' : '+ Mandje'}</button>
          ) : (
            <span className="mono" style={{ fontSize: 11, color: 'var(--ink-500)' }}>Niet beschikbaar</span>
          )}
        </div>
      </div>
    </article>
  );
}

// ============================================
// Product-detail
// ============================================
function ProductDetail({ id, navigate, wl }) {
  const { Placeholder, Reveal, Icon } = window.LM_UI;
  const [product, setProduct] = React.useState(null);
  const [qty, setQty] = React.useState(1);
  const [added, setAdded] = React.useState(false);
  const [imgIdx, setImgIdx] = React.useState(0);
  const [opts, setOpts] = React.useState({});
  const [allProducts, setAllProducts] = React.useState([]);
  const [reviewData, setReviewData] = React.useState({ reviews: [], rating: { avg: 0, count: 0 } });
  const [reviewPerm, setReviewPerm] = React.useState({ canReview: false, purchased: false, alreadyReviewed: false });
  const reviewsRef = React.useRef(null);

  const loadReviews = () => window.LM_SHOP.listReviews(id).then(setReviewData).catch(() => {});

  React.useEffect(() => {
    window.LM_SHOP.getProduct(id).then(p => { setProduct(p); setImgIdx(0); setOpts({}); }).catch(() => setProduct(false));
    window.LM_SHOP.listProducts().then(setAllProducts).catch(() => {});
    window.LM_SHOP.recentlyViewed.add(id);
    loadReviews();
    window.LM_SHOP.canReview(id).then(setReviewPerm).catch(() => {});
  }, [id]);

  if (product === null) return <div style={{ padding: 80, textAlign: 'center', color: 'var(--ink-500)' }}>Laden…</div>;
  if (product === false) return (
    <div style={{ padding: 80, textAlign: 'center', color: 'var(--ink-500)' }}>
      Product niet gevonden. <button className="btn btn-ghost btn-sm" onClick={() => navigate('webshop')}>Terug naar webshop</button>
    </div>
  );

  const images = shopImages(product);
  const hasVariants = shopHasVariants(product);
  const optionNames = (product.variantOptions || []).map(o => o.name);
  const selectedVariant = hasVariants ? shopVariantByOptions(product, opts) : null;
  const priceFrom = shopPriceFrom(product);
  const shownPrice = selectedVariant ? Number(selectedVariant.price) : priceFrom;
  const variantInStock = selectedVariant ? selectedVariant.inStock !== false : true;
  const productInStock = product.inStock !== false;
  const canBuy = productInStock && (!hasVariants || (!!selectedVariant && variantInStock));
  const mainImg = images[imgIdx] || images[0] || '';
  const related = (allProducts || []).filter(x => x.id !== product.id && product.category && x.category === product.category).slice(0, 3);

  const doAdd = (then) => {
    if (!canBuy) return;
    window.LM_SHOP.cart.add(product.id, qty, selectedVariant ? selectedVariant.id : null);
    if (then) then();
  };
  const addToCart = () => doAdd(() => { setAdded(true); setTimeout(() => setAdded(false), 2000); });
  const buyNow = () => doAdd(() => navigate('webshop/cart'));
  const setOpt = (name, val) => setOpts(prev => ({ ...prev, [name]: prev[name] === val ? undefined : val }));

  return (
    <section className="section">
      <div className="container">
        <Reveal>
          <button className="btn btn-ghost btn-sm" style={{ marginBottom: 24 }} onClick={() => navigate('webshop')}>
            <Icon.ArrowRight size={12} style={{ transform: 'rotate(180deg)' }} /> Terug naar webshop
          </button>
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 56, alignItems: 'flex-start' }} className="hero-grid">
            <div>
              <Placeholder label={product.name} src={mainImg} ratio="4/5" fit="contain" bg="#fff" style={{ borderRadius: 20, padding: 28, border: '1px solid var(--border)' }} />
              {images.length > 1 && (
                <div style={{ display: 'flex', gap: 8, marginTop: 12, flexWrap: 'wrap' }}>
                  {images.map((img, i) => (
                    <button key={i} onClick={() => setImgIdx(i)} style={{
                      width: 64, height: 64, borderRadius: 10, overflow: 'hidden', padding: 0, cursor: 'pointer',
                      border: i === imgIdx ? '2px solid var(--blue-600, #2563eb)' : '1.5px solid var(--ink-200)', background: 'var(--ink-50)',
                    }}>
                      <img src={(img || '').replace(/^(?!\/|https?:\/\/|\/\/|data:)/, '/')} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />
                    </button>
                  ))}
                </div>
              )}
            </div>
            <div>
              {product.category && (
                <div className="mono" style={{ fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--blue-600)', marginBottom: 12 }}>
                  {product.category}
                </div>
              )}
              <h1 className="display" style={{ fontSize: 'clamp(28px, 4.5vw, 48px)', margin: '0 0 12px', textTransform: 'none', letterSpacing: '-0.015em' }}>
                {product.name}
              </h1>
              <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap', marginBottom: 8 }}>
                {reviewData.rating && reviewData.rating.count > 0 ? (
                  <RatingSummary rating={reviewData.rating} size={16} onClick={() => reviewsRef.current?.scrollIntoView({ behavior: 'smooth' })} />
                ) : (
                  <span style={{ fontSize: 13, color: 'var(--ink-400)' }}>Nog geen beoordelingen</span>
                )}
                {wl && (!window.WNM || window.WNM.hasFeature('shop-wishlist')) && (
                  <button onClick={() => wl.toggle(product.id)} className="btn btn-ghost btn-sm" style={{ color: wl.ids.includes(product.id) ? '#e23' : 'var(--ink-700)' }}>
                    {wl.ids.includes(product.id) ? '♥ In verlanglijst' : '♡ Aan verlanglijst'}
                  </button>
                )}
              </div>
              {(selectedVariant?.sku || product.sku) && (
                <div className="mono" style={{ fontSize: 11, color: 'var(--ink-500)', marginBottom: 16 }}>
                  SKU: {selectedVariant?.sku || product.sku}
                </div>
              )}

              <div className="display" style={{ fontSize: 36, color: 'var(--ink-900)', margin: '20px 0', lineHeight: 1 }}>
                <PriceTag amount={shownPrice} compareAt={product.compareAtPrice} from={hasVariants && !selectedVariant} />
              </div>

              {product.description && (
                <p style={{ fontSize: 16, color: 'var(--ink-700)', lineHeight: 1.65, marginBottom: 24, whiteSpace: 'pre-wrap' }}>
                  {product.description}
                </p>
              )}

              {/* Variant-kiezer */}
              {hasVariants && (product.variantOptions || []).map(opt => (
                <div key={opt.name} style={{ marginBottom: 16 }}>
                  <label className="mono" style={{ fontSize: 11, color: 'var(--ink-500)', letterSpacing: '0.1em', textTransform: 'uppercase', display: 'block', marginBottom: 8 }}>{opt.name}</label>
                  <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                    {opt.values.map(val => {
                      const probe = { ...opts, [opt.name]: val };
                      const full = optionNames.every(n => probe[n]);
                      const matchV = full ? product.variants.find(v => optionNames.every(n => (v.options || {})[n] === probe[n])) : null;
                      const valOut = full && matchV && matchV.inStock === false;
                      const active = opts[opt.name] === val;
                      return (
                        <button key={val} onClick={() => setOpt(opt.name, val)} disabled={valOut} style={{
                          padding: '8px 14px', borderRadius: 8, cursor: valOut ? 'not-allowed' : 'pointer',
                          border: active ? '2px solid var(--ink-900)' : '1.5px solid var(--ink-200)',
                          background: active ? 'var(--teal-500)' : 'var(--surface)', color: active ? 'white' : (valOut ? 'var(--ink-300)' : 'var(--ink-900)'),
                          textDecoration: valOut ? 'line-through' : 'none', fontSize: 14, fontWeight: 600,
                        }}>{val}</button>
                      );
                    })}
                  </div>
                </div>
              ))}
              {hasVariants && !selectedVariant && (
                <p style={{ fontSize: 13, color: 'var(--ink-500)', marginBottom: 12 }}>
                  Kies {optionNames.map(n => n.toLowerCase()).join(' & ')} om verder te gaan.
                </p>
              )}

              {!productInStock ? (
                <div style={{ background: 'var(--ink-100)', padding: 20, borderRadius: 10, marginBottom: 16 }}>
                  <strong style={{ color: 'var(--ink-900)' }}>Uitverkocht</strong>
                  <p style={{ margin: '6px 0 0', fontSize: 14, color: 'var(--ink-500)' }}>Neem contact op om te vragen wanneer dit terug beschikbaar is.</p>
                </div>
              ) : (
                <>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
                    <label className="mono" style={{ fontSize: 11, color: 'var(--ink-500)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Aantal</label>
                    <button onClick={() => setQty(Math.max(1, qty - 1))} style={{ width: 36, height: 36, borderRadius: 8, border: '1.5px solid var(--ink-200)', background: 'var(--surface)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon.Minus /></button>
                    <span className="display" style={{ fontSize: 22, minWidth: 32, textAlign: 'center' }}>{qty}</span>
                    <button onClick={() => setQty(Math.min(50, qty + 1))} style={{ width: 36, height: 36, borderRadius: 8, border: '1.5px solid var(--ink-200)', background: 'var(--surface)', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon.Plus /></button>
                  </div>
                  <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                    <button className="btn btn-primary" onClick={buyNow} disabled={!canBuy}>Direct bestellen <Icon.ArrowRight /></button>
                    <button className="btn btn-ghost" onClick={addToCart} disabled={!canBuy}>
                      {added ? <><Icon.Check /> Toegevoegd!</> : <>+ Aan winkelmandje</>}
                    </button>
                  </div>
                  {hasVariants && selectedVariant && !variantInStock && (
                    <p style={{ fontSize: 13, color: '#c0392b', marginTop: 10 }}>Deze variant is momenteel uitverkocht.</p>
                  )}
                </>
              )}
            </div>
          </div>
          {/* Beoordelingen */}
          <div ref={reviewsRef} style={{ marginTop: 64 }}>
            <ReviewsSection
              productId={product.id}
              data={reviewData}
              perm={reviewPerm}
              authed={!!(wl && wl.authed)}
              navigate={navigate}
              onPosted={() => { loadReviews(); window.LM_SHOP.canReview(id).then(setReviewPerm).catch(() => {}); }}
            />
          </div>

          {related.length > 0 && (
            <div style={{ marginTop: 64 }}>
              <h2 className="display" style={{ fontSize: 24, marginBottom: 20, textTransform: 'none', letterSpacing: '-0.01em' }}>Misschien ook interessant</h2>
              <div className="grid grid-3">
                {related.map(rp => (
                  <ProductCard key={rp.id} product={rp} onOpen={() => navigate('webshop/product/' + rp.id)} wl={wl} />
                ))}
              </div>
            </div>
          )}
        </Reveal>

        <RecentlyViewed navigate={navigate} products={allProducts} wl={wl} excludeId={product.id} title="Eerder bekeken" />
      </div>
    </section>
  );
}

// ============================================
// Beoordelingen — lijst + formulier (enkel kopers)
// ============================================
function ReviewsSection({ productId, data, perm, authed, navigate, onPosted }) {
  const reviews = (data && data.reviews) || [];
  const rating = (data && data.rating) || { avg: 0, count: 0 };
  // Reviews-feature uit pakket gehaald? Dan helemaal niets tonen — niet de lijst, niet het formulier.
  // (Bestaande reviews blijven in de DB voor het geval het pakket later weer wordt opgewaardeerd.)
  if (window.WNM && !window.WNM.hasFeature('shop-reviews')) return null;
  const [rating1, setRating1] = React.useState(0);
  const [title, setTitle] = React.useState('');
  const [body, setBody] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [done, setDone] = React.useState(false);

  const submit = async (e) => {
    e.preventDefault();
    setErr('');
    if (!rating1) { setErr('Geef een aantal sterren.'); return; }
    if (!body.trim()) { setErr('Schrijf een korte beoordeling.'); return; }
    setBusy(true);
    try {
      await window.LM_SHOP.submitReview(productId, { rating: rating1, title: title.trim(), body: body.trim() });
      setDone(true); setRating1(0); setTitle(''); setBody('');
      if (onPosted) onPosted();
    } catch (ex) {
      const map = {
        not_purchased: 'Je kan dit product pas beoordelen nadat je het besteld hebt.',
        already_reviewed: 'Je hebt dit product al beoordeeld.',
        login_required: 'Meld je aan om een beoordeling te plaatsen.',
        body_required: 'Schrijf een korte beoordeling.',
      };
      setErr(map[ex.message] || 'Beoordeling kon niet verstuurd worden.');
    } finally { setBusy(false); }
  };

  const fmtDate = (d) => { try { return new Date(d).toLocaleDateString('nl-BE', { day: 'numeric', month: 'long', year: 'numeric' }); } catch { return ''; } };

  return (
    <div>
      <h2 className="display" style={{ fontSize: 24, marginBottom: 16, textTransform: 'none', letterSpacing: '-0.01em' }}>
        Beoordelingen {rating.count > 0 && <span style={{ fontWeight: 400, color: 'var(--ink-500)', fontSize: 18 }}>· {rating.avg.toFixed(1)}/5 ({rating.count})</span>}
      </h2>

      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.5fr) minmax(0, 1fr)', gap: 40, alignItems: 'flex-start' }} className="hero-grid">
        {/* Lijst */}
        <div>
          {reviews.length === 0 ? (
            <p style={{ color: 'var(--ink-500)' }}>Dit product heeft nog geen beoordelingen. Ben jij de eerste?</p>
          ) : reviews.map(r => (
            <div key={r.id} style={{ padding: '16px 0', borderBottom: '1px solid var(--border)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6, flexWrap: 'wrap' }}>
                <StarRating value={r.rating} size={15} />
                <strong style={{ fontSize: 14 }}>{r.title || ''}</strong>
              </div>
              <p style={{ margin: '0 0 8px', fontSize: 14, color: 'var(--ink-700)', lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{r.body}</p>
              <div style={{ fontSize: 12, color: 'var(--ink-500)' }}>
                {r.author}{r.verified && <span style={{ color: 'var(--blue-600)', marginLeft: 6 }}>✓ Geverifieerde aankoop</span>} · {fmtDate(r.createdAt)}
              </div>
            </div>
          ))}
        </div>

        {/* Formulier / status */}
        <div className="card" style={{ padding: 20 }}>
          {done ? (
            <div style={{ textAlign: 'center', padding: 8 }}>
              <div style={{ fontSize: 32, marginBottom: 8 }}>🙏</div>
              <strong style={{ display: 'block', marginBottom: 6 }}>Bedankt voor je beoordeling!</strong>
              <p style={{ fontSize: 13, color: 'var(--ink-500)', margin: 0 }}>Ze verschijnt zodra ze is goedgekeurd.</p>
            </div>
          ) : perm.canReview ? (
            <form onSubmit={submit}>
              <h3 className="display" style={{ fontSize: 18, margin: '0 0 12px', textTransform: 'none' }}>Schrijf een beoordeling</h3>
              <div style={{ marginBottom: 12 }}>
                <label className="label">Jouw score</label>
                <StarRating value={rating1} size={28} interactive onChange={setRating1} />
              </div>
              <div style={{ marginBottom: 12 }}>
                <label className="label">Titel (optioneel)</label>
                <input className="input" type="text" maxLength={120} value={title} onChange={e => setTitle(e.target.value)} placeholder="Bv. Mooie kwaliteit" />
              </div>
              <div style={{ marginBottom: 12 }}>
                <label className="label">Jouw ervaring</label>
                <textarea className="textarea" rows="4" maxLength={2000} value={body} onChange={e => setBody(e.target.value)} placeholder="Wat vond je van dit product?" />
              </div>
              {err && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 10, borderRadius: 8, marginBottom: 10, fontSize: 13 }}>{err}</div>}
              <button type="submit" className="btn btn-primary" disabled={busy} style={{ width: '100%', justifyContent: 'center' }}>
                {busy ? 'Versturen…' : 'Plaats beoordeling'}
              </button>
              <p style={{ fontSize: 12, color: 'var(--ink-500)', margin: '10px 0 0' }}>Je beoordeling verschijnt na goedkeuring.</p>
            </form>
          ) : !authed ? (
            <div>
              <h3 className="display" style={{ fontSize: 18, margin: '0 0 8px', textTransform: 'none' }}>Dit product beoordelen?</h3>
              <p style={{ fontSize: 13, color: 'var(--ink-500)', margin: '0 0 14px' }}>Enkel klanten die dit product gekocht hebben kunnen een beoordeling plaatsen. Meld je aan met je klantenaccount.</p>
              <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} onClick={() => navigate('webshop/login')}>Aanmelden</button>
            </div>
          ) : perm.alreadyReviewed ? (
            <div style={{ textAlign: 'center', padding: 8, color: 'var(--ink-500)', fontSize: 14 }}>Je hebt dit product al beoordeeld. Bedankt!</div>
          ) : (
            <div style={{ color: 'var(--ink-500)', fontSize: 14 }}>
              <h3 className="display" style={{ fontSize: 18, margin: '0 0 8px', textTransform: 'none', color: 'var(--ink-900)' }}>Beoordelingen</h3>
              Je kan dit product beoordelen zodra je het besteld hebt.
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ============================================
// Cart + checkout
// ============================================
function CartPage({ navigate, me, onLogin }) {
  const { Placeholder, Reveal, Icon } = window.LM_UI;
  const [cart, setCart] = React.useState(() => window.LM_SHOP.cart.items());
  const [products, setProducts] = React.useState({});
  const [notes, setNotes] = React.useState('');
  const [terms, setTerms] = React.useState(false);
  const [submitting, setSubmitting] = React.useState(false);
  const submittingRef = React.useRef(false);
  const [error, setError] = React.useState('');
  const [couponCode, setCouponCode] = React.useState('');
  const [coupon, setCoupon] = React.useState(null);
  const [couponError, setCouponError] = React.useState('');
  const [checkingCoupon, setCheckingCoupon] = React.useState(false);
  const [shipping, setShipping] = React.useState(null); // shop-config: verzending & afhaling
  const [deliveryMethod, setDeliveryMethod] = React.useState('pickup');
  const [addr, setAddr] = React.useState({ name: '', street: '', postalCode: '', city: '', country: 'België' });
  const [saveAddress, setSaveAddress] = React.useState(false);
  const [pickupDates, setPickupDates] = React.useState([]);          // door admin ingegeven ophaalmomenten
  const [pickupFieldMode, setPickupFieldMode] = React.useState('optional');
  const [pickupMomentId, setPickupMomentId] = React.useState('');
  const [classFieldMode, setClassFieldMode] = React.useState('optional'); // les/groep-veld aan/uit
  const [danceClassChoice, setDanceClassChoice] = React.useState('');     // gekozen les bij checkout (indien nog niet ingevuld)

  React.useEffect(() => {
    window.LM_SHOP.listProducts().then(list => {
      const map = {};
      list.forEach(p => { map[p.id] = p; });
      setProducts(map);
    });
    window.LM_SHOP.getConfig().then(cfg => {
      const sh = (cfg && cfg.shipping) || {};
      setShipping(sh);
      // Bestellingen worden altijd in de winkel afgehaald
      setDeliveryMethod('pickup');
      setPickupDates(Array.isArray(cfg.pickupDates) ? cfg.pickupDates : []);
      setPickupFieldMode((cfg.fields && cfg.fields.pickupMoment) || 'optional');
      setClassFieldMode((cfg.fields && cfg.fields.danceClass) || 'optional');
    });
  }, []);

  // Les/groep nog niet ingevuld voor deze danser? Dan vragen we ze bij het bestellen
  // (per dansjaar; nadien onthouden tot de admin de lesgroepen reset).
  const needClass = classFieldMode !== 'off' && !!me?.authed && !((me.customer?.danceClass || '').trim());
  const danceStyles = (window.LM_DATA && window.LM_DATA.DANCE_STYLES) || [];

  // Adres voorinvullen vanuit klantaccount
  React.useEffect(() => {
    const a = me?.customer?.address;
    if (a) setAddr({
      name: a.name || me?.customer?.name || '',
      street: a.street || '', postalCode: a.postalCode || '',
      city: a.city || '', country: a.country || 'België',
    });
    else if (me?.customer?.name) setAddr(prev => ({ ...prev, name: prev.name || me.customer.name }));
  }, [me]);

  const refreshCart = () => setCart(window.LM_SHOP.cart.items());

  const items = cart.map(it => {
    const product = products[it.productId];
    if (!product) return null;
    const variant = it.variantId && Array.isArray(product.variants) ? product.variants.find(v => v.id === it.variantId) : null;
    const unitPrice = variant ? Number(variant.price) : Number(product.price);
    const variantLabel = variant ? Object.values(variant.options || {}).filter(Boolean).join(' / ') : '';
    return { ...it, product, variant, unitPrice, variantLabel };
  }).filter(Boolean);
  const subtotal = items.reduce((sum, it) => sum + (it.unitPrice * it.qty), 0);
  const discount = coupon
    ? (coupon.type === 'percent' ? subtotal * (Number(coupon.value) / 100) : Math.min(subtotal, Number(coupon.value)))
    : 0;
  const afterDiscount = Math.max(0, subtotal - discount);
  // Verzendkost (enkel bij verzenden) — gratis vanaf drempel
  const shipEnabled = !!(shipping && shipping.enabled);
  const pickupEnabled = !shipping || shipping.pickupEnabled !== false;
  const freeThreshold = Number(shipping?.freeThreshold || 0);
  const shipBaseCost = Number(shipping?.cost || 0);
  const shippingCost = (deliveryMethod === 'shipping' && shipEnabled)
    ? ((freeThreshold > 0 && afterDiscount >= freeThreshold) ? 0 : shipBaseCost)
    : 0;
  const total = Math.max(0, afterDiscount + shippingCost);

  const checkCoupon = async () => {
    if (!couponCode.trim()) return;
    setCheckingCoupon(true);
    setCouponError('');
    try {
      const r = await fetch('/api/shop/coupon/check', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ code: couponCode.trim(), context: 'webshop' }),
      });
      const body = await r.json();
      if (body.valid) {
        setCoupon(body);
        setCouponError('');
      } else {
        setCoupon(null);
        setCouponError(body.reason === 'expired' ? 'Code is verlopen.'
                     : body.reason === 'not_started' ? 'Code is nog niet actief.'
                     : body.reason === 'exhausted' ? 'Code is opgebruikt.'
                     : body.reason === 'wrong_context' ? 'Deze code geldt niet voor de webshop.'
                     : 'Ongeldige code.');
      }
    } catch {
      setCouponError('Kon code niet controleren.');
    } finally { setCheckingCoupon(false); }
  };

  const placeOrder = async (e) => {
    e.preventDefault();
    // Hardlock tegen dubbel-klik vóór re-render — submittingRef wordt synchroon gezet
    if (submittingRef.current) return;
    if (items.length === 0) { setError('Je winkelmandje is leeg.'); return; }
    if (!me?.authed) { setError('Meld je aan om je bestelling af te ronden.'); return; }
    if (!terms) { setError('Je moet de voorwaarden accepteren.'); return; }
    if (deliveryMethod === 'shipping') {
      if (!addr.street.trim() || !addr.postalCode.trim() || !addr.city.trim()) {
        setError('Vul je volledige verzendadres in (straat, postcode en gemeente).');
        return;
      }
    }
    const askPickupMoment = deliveryMethod === 'pickup' && pickupFieldMode !== 'off' && pickupDates.length > 0;
    if (askPickupMoment && pickupFieldMode === 'required' && !pickupMomentId) {
      setError('Kies een ophaalmoment.');
      return;
    }
    if (needClass && !danceClassChoice.trim()) {
      setError('Kies de les/groep van de danser.');
      return;
    }
    setError('');
    submittingRef.current = true;
    setSubmitting(true);
    try {
      const result = await window.LM_SHOP.createOrder({
        items: items.map(i => ({ productId: i.productId, variantId: i.variantId || null, qty: i.qty })),
        notes,
        couponCode: coupon ? coupon.code : undefined,
        deliveryMethod,
        shippingAddress: deliveryMethod === 'shipping' ? {
          name: addr.name.trim(), street: addr.street.trim(),
          postalCode: addr.postalCode.trim(), city: addr.city.trim(),
          country: addr.country.trim() || 'België',
        } : undefined,
        saveAddress: deliveryMethod === 'shipping' ? !!saveAddress : undefined,
        pickupMomentId: askPickupMoment ? (pickupMomentId || undefined) : undefined,
        danceClass: needClass ? danceClassChoice.trim() : undefined,
      });
      window.LM_SHOP.cart.clear();
      if (result.checkoutUrl) {
        window.location.href = result.checkoutUrl;
      } else {
        navigate('webshop/return?id=' + result.orderId);
      }
    } catch (err) {
      setError(err.message || 'Bestelling kon niet aangemaakt worden.');
    } finally {
      setSubmitting(false);
      submittingRef.current = false;
    }
  };

  return (
    <section className="section">
      <div className="container" style={{ maxWidth: 1100 }}>
        <Reveal>
          <button className="btn btn-ghost btn-sm" style={{ marginBottom: 16 }} onClick={() => navigate('webshop')}>
            <Icon.ArrowRight size={12} style={{ transform: 'rotate(180deg)' }} /> Verder winkelen
          </button>
          <h1 className="display" style={{ fontSize: 'clamp(32px, 4.5vw, 48px)', margin: '0 0 24px', textTransform: 'none', letterSpacing: '-0.015em' }}>
            Winkelmandje
          </h1>
        </Reveal>

        {items.length === 0 ? (
          <div className="card" style={{ padding: 60, textAlign: 'center' }}>
            <p style={{ color: 'var(--ink-500)', marginBottom: 16 }}>Je winkelmandje is nog leeg.</p>
            <button className="btn btn-primary" onClick={() => navigate('webshop')}>Naar de webshop <Icon.ArrowRight /></button>
          </div>
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.4fr) minmax(0, 1fr)', gap: 32, alignItems: 'flex-start' }} className="hero-grid">
            <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
              {items.map(it => (
                <div key={it.productId + '|' + (it.variantId || '')} style={{ display: 'flex', gap: 16, padding: 16, borderBottom: '1px solid var(--border)', alignItems: 'center' }}>
                  <div style={{ width: 80, height: 80, flexShrink: 0, borderRadius: 10, overflow: 'hidden', background: 'var(--ink-50)' }}>
                    <Placeholder label={it.product.name} src={shopPrimaryImage(it.product)} ratio="auto" style={{ aspectRatio: 'unset', width: '100%', height: '100%', borderRadius: 0 }} />
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 600, marginBottom: 4 }}>{it.product.name}</div>
                    {it.variantLabel && <div style={{ fontSize: 12, color: 'var(--ink-700)', marginBottom: 2 }}>{it.variantLabel}</div>}
                    <div className="mono" style={{ fontSize: 11, color: 'var(--ink-500)' }}>{fmtEur(it.unitPrice)} × {it.qty}</div>
                  </div>
                  <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
                    <button onClick={() => { window.LM_SHOP.cart.setQty(it.productId, it.qty - 1, it.variantId); refreshCart(); }} disabled={it.qty <= 1} style={{ width: 28, height: 28, borderRadius: 6, border: '1px solid var(--ink-200)', background: 'var(--surface)', cursor: 'pointer' }}>−</button>
                    <span style={{ minWidth: 24, textAlign: 'center', fontWeight: 600 }}>{it.qty}</span>
                    <button onClick={() => { window.LM_SHOP.cart.setQty(it.productId, it.qty + 1, it.variantId); refreshCart(); }} style={{ width: 28, height: 28, borderRadius: 6, border: '1px solid var(--ink-200)', background: 'var(--surface)', cursor: 'pointer' }}>+</button>
                  </div>
                  <div style={{ fontWeight: 700, minWidth: 80, textAlign: 'right' }}>
                    {fmtEur(it.unitPrice * it.qty)}
                  </div>
                  <button onClick={() => { window.LM_SHOP.cart.remove(it.productId, it.variantId); refreshCart(); }} style={{ background: 'none', border: 'none', color: 'var(--ink-500)', cursor: 'pointer', fontSize: 18 }}>×</button>
                </div>
              ))}
              <div style={{ padding: 20, borderTop: '1px solid var(--border)' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, marginBottom: 6 }}>
                  <span style={{ color: 'var(--ink-500)' }}>Subtotaal</span>
                  <span>€{subtotal.toFixed(2)}</span>
                </div>
                {coupon && (
                  <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, marginBottom: 6, color: 'var(--blue-600)' }}>
                    <span>Korting ({coupon.code}{coupon.type === 'percent' ? ` -${coupon.value}%` : ''})</span>
                    <span>−€{discount.toFixed(2)}</span>
                  </div>
                )}
                {deliveryMethod === 'shipping' && shipEnabled && (
                  <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, marginBottom: 6 }}>
                    <span style={{ color: 'var(--ink-500)' }}>Verzending</span>
                    <span>{shippingCost > 0 ? `€${shippingCost.toFixed(2)}` : 'Gratis'}</span>
                  </div>
                )}
                {deliveryMethod === 'pickup' && (
                  <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, marginBottom: 6 }}>
                    <span style={{ color: 'var(--ink-500)' }}>Afhalen in de winkel</span>
                    <span>Gratis</span>
                  </div>
                )}
                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 18, fontWeight: 700, marginTop: 8 }}>
                  <span>Totaal</span>
                  <span>€{total.toFixed(2)}</span>
                </div>
              </div>
            </div>

            <div className="card" style={{ padding: 24 }}>
              <h3 className="display" style={{ fontSize: 20, margin: '0 0 16px', textTransform: 'none', letterSpacing: '-0.01em' }}>
                Afronden
              </h3>

              {!me?.authed ? (
                <div>
                  <div style={{ background: 'var(--gradient-soft)', padding: 16, borderRadius: 10, marginBottom: 16 }}>
                    <strong style={{ fontSize: 14, color: 'var(--ink-900)' }}>Aanmelden vereist</strong>
                    <p style={{ margin: '6px 0 0', fontSize: 13, color: 'var(--ink-700)' }}>
                      Om je bestelling te plaatsen heb je een klantenaccount nodig. Zo kan je je bestellingen later opvolgen.
                    </p>
                  </div>
                  <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center', marginBottom: 8 }} onClick={() => navigate('webshop/login')}>
                    Aanmelden <Icon.ArrowRight />
                  </button>
                  <button className="btn btn-ghost" style={{ width: '100%', justifyContent: 'center' }} onClick={() => navigate('webshop/register')}>
                    Nieuw account aanmaken
                  </button>
                </div>
              ) : (
                <form onSubmit={placeOrder}>
                  <div style={{ background: 'var(--ink-50)', padding: 14, borderRadius: 10, marginBottom: 16 }}>
                    <div className="mono" style={{ fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--ink-500)', marginBottom: 4 }}>
                      Bestelling voor
                    </div>
                    <div style={{ fontSize: 15, fontWeight: 600 }}>{me.customer.name}</div>
                    <div style={{ fontSize: 13, color: 'var(--ink-500)' }}>{me.customer.email}{me.customer.phone && ' · ' + me.customer.phone}</div>
                    {(me.customer.dancerName || me.customer.danceClass) && (
                      <div style={{ fontSize: 13, color: 'var(--ink-700)', marginTop: 4 }}>
                        {me.customer.dancerName && <>Danser: <strong>{me.customer.dancerName}</strong></>}
                        {me.customer.danceClass && <> · {me.customer.danceClass}</>}
                      </div>
                    )}
                  </div>

                  {/* Les/groep — gevraagd zolang nog niet ingevuld voor deze danser (per dansjaar) */}
                  {needClass && (
                    <div style={{ marginBottom: 16, padding: 14, border: '1px solid var(--blue-600)', borderRadius: 10, background: 'var(--gradient-soft)' }}>
                      <label className="label">Les / groep van de danser *</label>
                      <select className="input" required value={danceClassChoice} onChange={e => setDanceClassChoice(e.target.value)}>
                        <option value="">— Kies een les —</option>
                        {danceStyles.map(s => <option key={s.id} value={s.name}>{s.name}</option>)}
                      </select>
                      <p style={{ fontSize: 12, color: 'var(--ink-700)', margin: '6px 0 0' }}>
                        We vragen dit bij het begin van elk dansjaar. Daarna wordt het bewaard voor je volgende bestellingen.
                      </p>
                    </div>
                  )}

                  {/* Ophaalmoment — enkel bij afhalen, uit de door admin ingegeven lijst */}
                  {deliveryMethod === 'pickup' && pickupFieldMode !== 'off' && pickupDates.length > 0 && (
                    <div style={{ marginBottom: 16 }}>
                      <label className="label">Ophaalmoment{pickupFieldMode === 'required' ? ' *' : ' (optioneel)'}</label>
                      <div style={{ display: 'grid', gap: 8 }}>
                        {pickupDates.map(d => (
                          <label key={d.id} style={{ display: 'flex', gap: 10, alignItems: 'center', padding: 12, border: '1px solid ' + (pickupMomentId === d.id ? 'var(--blue-600)' : 'var(--border)'), borderRadius: 10, cursor: 'pointer', background: pickupMomentId === d.id ? 'var(--gradient-soft)' : 'var(--surface)' }}>
                            <input type="radio" name="pickupMoment" checked={pickupMomentId === d.id} onChange={() => setPickupMomentId(d.id)} />
                            <span style={{ fontSize: 14 }}>{d.label}</span>
                          </label>
                        ))}
                      </div>
                      <p style={{ fontSize: 12, color: 'var(--ink-500)', margin: '6px 0 0' }}>Kies wanneer je je bestelling komt afhalen.</p>
                    </div>
                  )}

                  {/* Verzendadres */}
                  {deliveryMethod === 'shipping' && shipEnabled && (
                    <div style={{ marginBottom: 16, padding: 14, border: '1px solid var(--border)', borderRadius: 10 }}>
                      <div className="mono" style={{ fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--ink-500)', marginBottom: 10 }}>
                        Verzendadres
                      </div>
                      <div style={{ marginBottom: 10 }}>
                        <label className="label">Naam / t.a.v.</label>
                        <input className="input" type="text" value={addr.name} onChange={e => setAddr({ ...addr, name: e.target.value })} placeholder="Voor- en achternaam" />
                      </div>
                      <div style={{ marginBottom: 10 }}>
                        <label className="label">Straat en huisnummer *</label>
                        <input className="input" type="text" value={addr.street} onChange={e => setAddr({ ...addr, street: e.target.value })} placeholder="Bv. Dorpsstraat 12" required />
                      </div>
                      <div style={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 10, marginBottom: 10 }}>
                        <div>
                          <label className="label">Postcode *</label>
                          <input className="input" type="text" value={addr.postalCode} onChange={e => setAddr({ ...addr, postalCode: e.target.value })} placeholder="9000" required />
                        </div>
                        <div>
                          <label className="label">Gemeente *</label>
                          <input className="input" type="text" value={addr.city} onChange={e => setAddr({ ...addr, city: e.target.value })} placeholder="Gent" required />
                        </div>
                      </div>
                      <div style={{ marginBottom: 10 }}>
                        <label className="label">Land</label>
                        <input className="input" type="text" value={addr.country} onChange={e => setAddr({ ...addr, country: e.target.value })} />
                      </div>
                      <label style={{ display: 'flex', gap: 8, alignItems: 'center', fontSize: 13, cursor: 'pointer' }}>
                        <input type="checkbox" checked={saveAddress} onChange={e => setSaveAddress(e.target.checked)} />
                        <span>Bewaar dit adres bij mijn account voor volgende bestellingen</span>
                      </label>
                    </div>
                  )}

                  <div style={{ marginBottom: 16 }}>
                    <label className="label">Kortingscode (optioneel)</label>
                    {coupon ? (
                      <div style={{ display: 'flex', gap: 8, alignItems: 'center', padding: 10, background: 'var(--gradient-soft)', borderRadius: 8 }}>
                        <span style={{ fontWeight: 600, fontFamily: 'JetBrains Mono, monospace' }}>{coupon.code}</span>
                        <span style={{ flex: 1, fontSize: 13, color: 'var(--ink-700)' }}>
                          {coupon.type === 'percent' ? `${coupon.value}% korting` : `€${coupon.value} korting`}
                          {coupon.description && ` · ${coupon.description}`}
                        </span>
                        <button type="button" onClick={() => { setCoupon(null); setCouponCode(''); }} style={{ background: 'none', border: 'none', color: 'var(--ink-500)', cursor: 'pointer', fontSize: 18 }}>×</button>
                      </div>
                    ) : (
                      <>
                        <div style={{ display: 'flex', gap: 8 }}>
                          <input type="text" value={couponCode}
                            onChange={e => setCouponCode(e.target.value.toUpperCase())}
                            placeholder="bv. ZOMER2026"
                            maxLength={30}
                            style={{ flex: 1, padding: 10, border: '1px solid var(--border)', borderRadius: 6, fontFamily: 'JetBrains Mono, monospace', textTransform: 'uppercase' }} />
                          <button type="button" onClick={checkCoupon} disabled={!couponCode.trim() || checkingCoupon}
                            className="btn btn-ghost btn-sm">
                            {checkingCoupon ? '…' : 'Toepassen'}
                          </button>
                        </div>
                        {couponError && <small style={{ color: '#9b1c1c', display: 'block', marginTop: 4 }}>{couponError}</small>}
                      </>
                    )}
                  </div>

                  <label style={{ display: 'flex', gap: 10, alignItems: 'flex-start', marginBottom: 14, cursor: 'pointer', fontSize: 13 }}>
                    <input type="checkbox" checked={terms} onChange={e => setTerms(e.target.checked)} style={{ marginTop: 3 }} />
                    <span>Ik ga akkoord met de <a href="#/algemene-voorwaarden" target="_blank" onClick={e => e.stopPropagation()} style={{ color: 'var(--blue-600)', textDecoration: 'underline' }}>algemene voorwaarden</a> en het <a href="#/privacybeleid" target="_blank" onClick={e => e.stopPropagation()} style={{ color: 'var(--blue-600)', textDecoration: 'underline' }}>privacybeleid</a>.</span>
                  </label>

                  {error && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 10, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{error}</div>}

                  <button type="submit" className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={submitting || !terms}>
                    {submitting ? 'Bezig…' : `Betaal €${total.toFixed(2)} via Mollie`} <Icon.ArrowRight />
                  </button>
                </form>
              )}
            </div>
          </div>
        )}
      </div>
    </section>
  );
}

// ============================================
// Klantenaccount: login / register / profiel
// ============================================
function LoginPage({ navigate, onSuccess }) {
  const { Reveal, Icon } = window.LM_UI;
  const [form, setForm] = React.useState({ email: '', password: '' });
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  const submit = async (e) => {
    e.preventDefault();
    setErr(''); setBusy(true);
    try {
      const r = await fetch('/api/shop/account/login', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(form),
      });
      const body = await r.json().catch(() => ({}));
      if (!r.ok) {
        // Map gekende server-fouten naar gebruiksvriendelijke berichten
        const friendly = {
          rate_limited: 'Te veel pogingen. Wacht even voor je opnieuw probeert.',
          invalid_credentials: 'E-mail of wachtwoord onjuist.',
          invalid: 'Vul beide velden in.',
        };
        throw new Error(friendly[body.error] || body.message || 'E-mail of wachtwoord onjuist.');
      }
      if (onSuccess) onSuccess(); else navigate('webshop');
    } catch (ex) { setErr(ex.message); }
    finally { setBusy(false); }
  };

  return (
    <section className="section">
      <div className="container" style={{ maxWidth: 460 }}>
        <Reveal>
          <div className="card" style={{ padding: 32 }}>
            <h1 className="display" style={{ fontSize: 28, margin: '0 0 8px', textTransform: 'none', letterSpacing: '-0.01em' }}>Aanmelden</h1>
            <p style={{ color: 'var(--ink-500)', margin: '0 0 24px', fontSize: 14 }}>Meld je aan met je klant-account.</p>
            <form onSubmit={submit}>
              <div style={{ marginBottom: 14 }}>
                <label className="label">E-mail</label>
                <input className="input" type="email" required value={form.email} onChange={e => setForm({ ...form, email: e.target.value })} autoFocus />
              </div>
              <div style={{ marginBottom: 18 }}>
                <label className="label">Wachtwoord</label>
                <input className="input" type="password" required value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} />
              </div>
              {err && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 10, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{err}</div>}
              <button type="submit" className="btn btn-primary" disabled={busy} style={{ width: '100%', justifyContent: 'center' }}>
                {busy ? 'Bezig…' : 'Aanmelden'} <Icon.ArrowRight />
              </button>
              <p style={{ marginTop: 12, textAlign: 'center', fontSize: 13, color: 'var(--ink-500)' }}>
                <a href="#/webshop/forgot-password" onClick={e => { e.preventDefault(); navigate('webshop/forgot-password'); }} style={{ color: 'var(--blue-600)', textDecoration: 'underline' }}>Wachtwoord vergeten?</a>
              </p>
              <p style={{ marginTop: 6, textAlign: 'center', fontSize: 13, color: 'var(--ink-500)' }}>
                Nog geen account? <a href="#/webshop/register" onClick={e => { e.preventDefault(); navigate('webshop/register'); }} style={{ color: 'var(--blue-600)', textDecoration: 'underline' }}>Maak er één aan</a>
              </p>
            </form>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// 'Wachtwoord vergeten' — verstuurt een reset-link naar het opgegeven e-mailadres.
// Vertelt nooit of het e-mailadres bestaat (anti-enumeration).
function ForgotPasswordPage({ navigate }) {
  const { Reveal, Icon } = window.LM_UI;
  const [email, setEmail] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [done, setDone] = React.useState(false);
  const [err, setErr] = React.useState('');

  const submit = async (e) => {
    e.preventDefault();
    setErr(''); setBusy(true);
    try {
      const r = await fetch('/api/shop/account/request-reset', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: email.trim() }),
      });
      const body = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(body.message || 'Aanvraag kon niet verzonden worden.');
      setDone(true);
    } catch (ex) { setErr(ex.message); }
    finally { setBusy(false); }
  };

  return (
    <section className="section">
      <div className="container" style={{ maxWidth: 460 }}>
        <Reveal>
          <div className="card" style={{ padding: 32 }}>
            <h1 className="display" style={{ fontSize: 26, margin: '0 0 8px', textTransform: 'none', letterSpacing: '-0.01em' }}>Wachtwoord vergeten?</h1>
            <p style={{ color: 'var(--ink-500)', margin: '0 0 24px', fontSize: 14 }}>Geef je e-mailadres in — we sturen je een link om een (nieuw) wachtwoord in te stellen.</p>
            {done ? (
              <div style={{ background: '#dcfce7', color: '#166534', padding: 14, borderRadius: 8, fontSize: 14 }}>
                ✓ Als dit e-mailadres bij ons bekend is, ontvang je binnen enkele minuten een link in je mailbox. Check ook je spam-folder. De link is 14 dagen geldig.
              </div>
            ) : (
              <form onSubmit={submit}>
                <div style={{ marginBottom: 18 }}>
                  <label className="label">E-mail</label>
                  <input className="input" type="email" required value={email} onChange={e => setEmail(e.target.value)} autoFocus />
                </div>
                {err && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 10, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{err}</div>}
                <button type="submit" className="btn btn-primary" disabled={busy} style={{ width: '100%', justifyContent: 'center' }}>
                  {busy ? 'Bezig…' : 'Stuur me een link'} <Icon.ArrowRight />
                </button>
              </form>
            )}
            <p style={{ marginTop: 16, textAlign: 'center', fontSize: 13, color: 'var(--ink-500)' }}>
              <a href="#/webshop/login" onClick={e => { e.preventDefault(); navigate('webshop/login'); }} style={{ color: 'var(--blue-600)', textDecoration: 'underline' }}>← Terug naar aanmelden</a>
            </p>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// Wachtwoord instellen via een mailing-link (?t=TOKEN). Verifieert de token eerst,
// laat dan toe om een (nieuw) wachtwoord te kiezen — meteen ingelogd na succes.
function SetPasswordPage({ navigate, onSuccess }) {
  const { Reveal, Icon } = window.LM_UI;
  const getToken = () => { const m = (window.location.hash || '').match(/[?&]t=([^&]+)/); return m ? decodeURIComponent(m[1]) : ''; };
  const [token] = React.useState(getToken);
  const [info, setInfo] = React.useState(null); // { email, name, hasPassword } of null bij verifying
  const [checking, setChecking] = React.useState(true);
  const [checkErr, setCheckErr] = React.useState('');
  const [pw, setPw] = React.useState('');
  const [pw2, setPw2] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    if (!token) { setCheckErr('Geen toegangslink gevonden. Vraag een nieuwe link aan via "Wachtwoord vergeten?".'); setChecking(false); return; }
    fetch('/api/shop/account/check-reset?t=' + encodeURIComponent(token))
      .then(async r => {
        const body = await r.json().catch(() => ({}));
        if (!r.ok) throw new Error(body.message || 'Deze link is niet (meer) geldig.');
        return body;
      })
      .then(setInfo)
      .catch(ex => setCheckErr(ex.message))
      .finally(() => setChecking(false));
  }, [token]);

  const submit = async (e) => {
    e.preventDefault();
    setErr('');
    if (pw.length < 8) { setErr('Wachtwoord moet minstens 8 tekens hebben.'); return; }
    if (pw !== pw2) { setErr('De twee wachtwoorden komen niet overeen.'); return; }
    setBusy(true);
    try {
      const r = await fetch('/api/shop/account/set-password', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, password: pw }),
      });
      const body = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(body.message || 'Instellen mislukt.');
      if (onSuccess) onSuccess(); else navigate('webshop/account');
    } catch (ex) { setErr(ex.message); }
    finally { setBusy(false); }
  };

  return (
    <section className="section">
      <div className="container" style={{ maxWidth: 460 }}>
        <Reveal>
          <div className="card" style={{ padding: 32 }}>
            <h1 className="display" style={{ fontSize: 26, margin: '0 0 8px', textTransform: 'none', letterSpacing: '-0.01em' }}>Stel je wachtwoord in</h1>
            {checking ? (
              <p style={{ color: 'var(--ink-500)' }}>Even controleren…</p>
            ) : checkErr ? (
              <>
                <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 14, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{checkErr}</div>
                <a href="#/webshop/forgot-password" onClick={e => { e.preventDefault(); navigate('webshop/forgot-password'); }} className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }}>
                  Vraag een nieuwe link aan <Icon.ArrowRight />
                </a>
              </>
            ) : (
              <>
                <p style={{ color: 'var(--ink-500)', margin: '0 0 20px', fontSize: 14 }}>
                  {info?.hasPassword ? 'Kies een nieuw wachtwoord' : 'Welkom! Stel een wachtwoord in om in te loggen op de webshop'} — voor account <strong>{info?.email}</strong>.
                </p>
                <form onSubmit={submit}>
                  <div style={{ marginBottom: 14 }}>
                    <label className="label">Nieuw wachtwoord (min. 8 tekens)</label>
                    <input className="input" type="password" required minLength={8} value={pw} onChange={e => setPw(e.target.value)} autoFocus />
                  </div>
                  <div style={{ marginBottom: 18 }}>
                    <label className="label">Wachtwoord bevestigen</label>
                    <input className="input" type="password" required minLength={8} value={pw2} onChange={e => setPw2(e.target.value)} />
                  </div>
                  {err && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 10, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{err}</div>}
                  <button type="submit" className="btn btn-primary" disabled={busy} style={{ width: '100%', justifyContent: 'center' }}>
                    {busy ? 'Bezig…' : 'Wachtwoord instellen en aanmelden'} <Icon.ArrowRight />
                  </button>
                </form>
              </>
            )}
          </div>
        </Reveal>
      </div>
    </section>
  );
}

function RegisterPage({ navigate, onSuccess }) {
  const { Reveal, Icon } = window.LM_UI;
  const [form, setForm] = React.useState({ name: '', email: '', password: '', phone: '', dancerName: '', danceClass: '' });
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');
  const [fields, setFields] = React.useState({ dancerName: 'optional', danceClass: 'optional' });
  const danceStyles = (window.LM_DATA && window.LM_DATA.DANCE_STYLES) || [];

  React.useEffect(() => {
    window.LM_SHOP.getConfig().then(cfg => setFields(cfg.fields || {})).catch(() => {});
  }, []);

  const submit = async (e) => {
    e.preventDefault();
    setErr(''); setBusy(true);
    try {
      const r = await fetch('/api/shop/account/register', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(form),
      });
      const body = await r.json();
      if (!r.ok) {
        const map = {
          email_taken: 'Er bestaat al een account met dit adres.',
          dancer_name_required: 'Vul de naam van de danser in.',
          dance_class_required: 'Kies de les/groep van de danser.',
          password_too_short: 'Wachtwoord moet minstens 8 tekens hebben.',
        };
        throw new Error(map[body.error] || body.message || 'Aanmaken mislukt.');
      }
      if (onSuccess) onSuccess(); else navigate('webshop');
    } catch (ex) { setErr(ex.message); }
    finally { setBusy(false); }
  };

  return (
    <section className="section">
      <div className="container" style={{ maxWidth: 560 }}>
        <Reveal>
          <div className="card" style={{ padding: 32 }}>
            <h1 className="display" style={{ fontSize: 28, margin: '0 0 8px', textTransform: 'none', letterSpacing: '-0.01em' }}>Account aanmaken</h1>
            <p style={{ color: 'var(--ink-500)', margin: '0 0 24px', fontSize: 14 }}>Met een account kan je sneller bestellen en je bestellingen opvolgen.</p>
            <form onSubmit={submit}>
              <div className="grid grid-2" style={{ marginBottom: 14 }}>
                <div><label className="label">Naam ouder / besteller *</label><input className="input" required value={form.name} onChange={e => setForm({ ...form, name: e.target.value })} /></div>
                <div><label className="label">Telefoon</label><input className="input" value={form.phone} onChange={e => setForm({ ...form, phone: e.target.value })} placeholder="04XX..." /></div>
              </div>

              {/* Dansergegevens — getoond/verplicht volgens admin-instelling */}
              {(fields.dancerName !== 'off' || fields.danceClass !== 'off') && (
                <div className="grid grid-2" style={{ marginBottom: 14 }}>
                  {fields.dancerName !== 'off' && (
                    <div>
                      <label className="label">Naam danser{fields.dancerName === 'required' ? ' *' : ''}</label>
                      <input className="input" required={fields.dancerName === 'required'} value={form.dancerName} onChange={e => setForm({ ...form, dancerName: e.target.value })} placeholder="Voor- en achternaam danser" />
                    </div>
                  )}
                  {fields.danceClass !== 'off' && (
                    <div>
                      <label className="label">Les / groep van de danser{fields.danceClass === 'required' ? ' *' : ''}</label>
                      <select className="input" required={fields.danceClass === 'required'} value={form.danceClass} onChange={e => setForm({ ...form, danceClass: e.target.value })}>
                        <option value="">— Kies een les —</option>
                        {danceStyles.map(s => <option key={s.id} value={s.name}>{s.name}</option>)}
                      </select>
                    </div>
                  )}
                </div>
              )}

              <div style={{ marginBottom: 14 }}>
                <label className="label">E-mail *</label>
                <input className="input" type="email" required value={form.email} onChange={e => setForm({ ...form, email: e.target.value })} />
              </div>
              <div style={{ marginBottom: 18 }}>
                <label className="label">Wachtwoord (min. 8 tekens) *</label>
                <input className="input" type="password" required minLength="8" value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} />
              </div>
              {err && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 10, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{err}</div>}
              <button type="submit" className="btn btn-primary" disabled={busy} style={{ width: '100%', justifyContent: 'center' }}>
                {busy ? 'Bezig…' : 'Account aanmaken'} <Icon.ArrowRight />
              </button>
              <p style={{ marginTop: 16, textAlign: 'center', fontSize: 13, color: 'var(--ink-500)' }}>
                Al een account? <a href="#/webshop/login" onClick={e => { e.preventDefault(); navigate('webshop/login'); }} style={{ color: 'var(--blue-600)', textDecoration: 'underline' }}>Aanmelden</a>
              </p>
            </form>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

function AccountPage({ navigate, me, onChange }) {
  const { Reveal, Icon } = window.LM_UI;
  const [orders, setOrders] = React.useState(null);
  const [editing, setEditing] = React.useState(false);
  const [profile, setProfile] = React.useState(me?.customer || {});

  React.useEffect(() => {
    if (me?.authed) {
      fetch('/api/shop/account/orders').then(r => r.json()).then(d => setOrders(d.orders || []));
    }
    setProfile(me?.customer || {});
  }, [me]);

  if (!me) return <div style={{ padding: 80, textAlign: 'center' }}>Laden…</div>;
  if (!me.authed) {
    return (
      <section className="section">
        <div className="container" style={{ maxWidth: 460, textAlign: 'center' }}>
          <p style={{ color: 'var(--ink-500)', marginBottom: 16 }}>Niet aangemeld.</p>
          <button className="btn btn-primary" onClick={() => navigate('webshop/login')}>Aanmelden</button>
        </div>
      </section>
    );
  }

  const logout = async () => {
    await fetch('/api/shop/account/logout', { method: 'POST' });
    onChange();
    navigate('webshop');
  };

  const danceStyles = (window.LM_DATA && window.LM_DATA.DANCE_STYLES) || [];

  const saveProfile = async () => {
    await fetch('/api/shop/account/me', {
      method: 'PATCH', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: profile.name, phone: profile.phone, dancerName: profile.dancerName, danceClass: profile.danceClass }),
    });
    setEditing(false);
    onChange();
  };

  return (
    <section className="section">
      <div className="container" style={{ maxWidth: 900 }}>
        <Reveal>
          <h1 className="display" style={{ fontSize: 'clamp(28px, 4vw, 40px)', margin: '0 0 24px', textTransform: 'none', letterSpacing: '-0.015em' }}>
            Mijn account
          </h1>
          <div className="card" style={{ padding: 24, marginBottom: 16 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
              <h2 style={{ fontSize: 18, margin: 0 }}>Profiel</h2>
              <button className="btn btn-ghost btn-sm" onClick={() => setEditing(!editing)}>{editing ? 'Annuleer' : 'Bewerken'}</button>
            </div>
            {editing ? (
              <>
                <div className="grid grid-2" style={{ marginBottom: 12 }}>
                  <div><label className="label">Naam</label><input className="input" value={profile.name || ''} onChange={e => setProfile({ ...profile, name: e.target.value })} /></div>
                  <div><label className="label">Telefoon</label><input className="input" value={profile.phone || ''} onChange={e => setProfile({ ...profile, phone: e.target.value })} /></div>
                </div>
                <div className="grid grid-2" style={{ marginBottom: 12 }}>
                  <div><label className="label">Naam danser</label><input className="input" value={profile.dancerName || ''} onChange={e => setProfile({ ...profile, dancerName: e.target.value })} /></div>
                  <div>
                    <label className="label">Les / groep van de danser</label>
                    <select className="input" value={profile.danceClass || ''} onChange={e => setProfile({ ...profile, danceClass: e.target.value })}>
                      <option value="">— Kies een les —</option>
                      {danceStyles.map(s => <option key={s.id} value={s.name}>{s.name}</option>)}
                      {profile.danceClass && !danceStyles.some(s => s.name === profile.danceClass) && <option value={profile.danceClass}>{profile.danceClass}</option>}
                    </select>
                  </div>
                </div>
                <button className="btn btn-primary btn-sm" onClick={saveProfile}>Opslaan</button>
              </>
            ) : (
              <div style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', columnGap: 20, rowGap: 6, fontSize: 14 }}>
                <span className="mono" style={{ color: 'var(--ink-500)' }}>Naam</span><span>{me.customer.name}</span>
                <span className="mono" style={{ color: 'var(--ink-500)' }}>E-mail</span><span>{me.customer.email}</span>
                <span className="mono" style={{ color: 'var(--ink-500)' }}>Telefoon</span><span>{me.customer.phone || '—'}</span>
                <span className="mono" style={{ color: 'var(--ink-500)' }}>Naam danser</span><span>{me.customer.dancerName || '—'}</span>
                <span className="mono" style={{ color: 'var(--ink-500)' }}>Les / groep</span><span>{me.customer.danceClass || '—'}</span>
              </div>
            )}
          </div>

          <div className="card" style={{ padding: 24, marginBottom: 16 }}>
            <h2 style={{ fontSize: 18, margin: '0 0 16px' }}>Mijn bestellingen</h2>
            {orders === null && <p style={{ color: 'var(--ink-500)' }}>Laden…</p>}
            {orders && orders.length === 0 && <p style={{ color: 'var(--ink-500)', fontSize: 14 }}>Nog geen bestellingen geplaatst.</p>}
            {orders && orders.map(o => (
              <div key={o.id} style={{ borderBottom: '1px solid var(--border)', padding: '14px 0' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
                  <div>
                    <div className="mono" style={{ fontSize: 11, color: 'var(--ink-500)' }}>{new Date(o.createdAt).toLocaleString('nl-BE')}</div>
                    <div style={{ fontWeight: 600 }}>{(o.items || []).map(i => `${i.qty}× ${i.name}`).join(', ')}</div>
                  </div>
                  <div style={{ textAlign: 'right' }}>
                    <div className="display" style={{ fontSize: 18 }}>€{o.total}</div>
                    <div style={{ fontSize: 12 }}>
                      {o.status === 'paid' ? <span style={{ color: 'var(--teal-700)', fontWeight: 600 }}>✓ Betaald</span> : <span style={{ color: 'var(--ink-500)' }}>{o.status}</span>}
                      {o.pickup?.status === 'ready' && <span style={{ marginLeft: 8, color: '#854d0e', fontWeight: 600 }}>📦 Klaar om af te halen</span>}
                      {o.pickup?.status === 'picked_up' && <span style={{ marginLeft: 8, color: 'var(--teal-700)', fontWeight: 600 }}>✓ Opgehaald</span>}
                    </div>
                  </div>
                </div>
              </div>
            ))}
          </div>

          <button className="btn btn-ghost" onClick={logout}>Afmelden</button>
        </Reveal>
      </div>
    </section>
  );
}

// ============================================
// Order return (na Mollie checkout)
// ============================================
function OrderReturnPage({ navigate }) {
  const { Reveal, Icon } = window.LM_UI;
  const getId = () => {
    const m = (window.location.hash || '').match(/[?&]id=([^&]+)/);
    return m ? decodeURIComponent(m[1]) : '';
  };
  const [id] = React.useState(getId);
  const [status, setStatus] = React.useState(null);
  const [polling, setPolling] = React.useState(true);
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    if (!id) { setErr('Geen referentie gevonden.'); setPolling(false); return; }
    let cancelled = false, timer = null, tries = 0;
    const tick = async () => {
      tries++;
      try {
        const s = await window.LM_SHOP.orderStatus(id);
        if (cancelled) return;
        setStatus(s);
        if (['paid', 'failed', 'canceled', 'expired'].includes(s.paymentStatus) || tries >= 12) {
          setPolling(false); return;
        }
      } catch {
        if (cancelled) return;
        setErr('Status kon niet opgehaald worden.'); setPolling(false); return;
      }
      timer = setTimeout(tick, 2500);
    };
    tick();
    return () => { cancelled = true; if (timer) clearTimeout(timer); };
  }, [id]);

  const isPaid = status?.paymentStatus === 'paid';
  const isFailed = ['failed', 'canceled', 'expired'].includes(status?.paymentStatus);

  return (
    <section className="section" style={{ minHeight: '60vh' }}>
      <div className="container" style={{ maxWidth: 680 }}>
        <Reveal>
          <div className="card" style={{ padding: '48px 40px', textAlign: 'center' }}>
            {err ? (
              <h2 className="display" style={{ fontSize: 28, margin: '0 0 12px', textTransform: 'none' }}>{err}</h2>
            ) : polling ? (
              <>
                <div style={{ width: 56, height: 56, borderRadius: '50%', border: '4px solid var(--ink-100)', borderTopColor: 'var(--blue-500)', margin: '0 auto 20px', animation: 'spin 1s linear infinite' }} />
                <h2 className="display" style={{ fontSize: 26, margin: '0 0 12px', textTransform: 'none' }}>We bevestigen je betaling…</h2>
                <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
              </>
            ) : isPaid ? (
              <>
                <div style={{ width: 72, height: 72, borderRadius: '50%', background: 'var(--gradient)', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 24px' }}>
                  <Icon.Check size={32} />
                </div>
                <h2 className="display" style={{ fontSize: 30, margin: '0 0 12px', textTransform: 'none' }}>Bedankt voor je bestelling!</h2>
                <p style={{ fontSize: 15, color: 'var(--ink-500)' }}>
                  Je betaling van <strong>€{status.total}</strong> via {status.method || 'Mollie'} is bevestigd.<br />
                  {status.delivery?.method === 'shipping'
                    ? 'We bereiden je bestelling voor en verzenden ze naar het opgegeven adres.'
                    : 'We bereiden je bestelling voor — je krijgt bericht zodra ze klaar ligt om af te halen.'}
                </p>
                {status.delivery?.method === 'pickup' && status.delivery?.pickupMoment && (
                  <div style={{ display: 'inline-block', margin: '14px auto 0', padding: '10px 16px', background: 'var(--gradient-soft)', borderRadius: 10, fontSize: 14, color: 'var(--ink-900)' }}>
                    📅 Ophaalmoment: <strong>{status.delivery.pickupMoment.label}</strong>
                  </div>
                )}
                {status.delivery?.method === 'shipping' && status.delivery?.address && (
                  <div style={{ display: 'inline-block', textAlign: 'left', margin: '14px auto 0', padding: '12px 16px', background: 'var(--ink-50)', borderRadius: 10, fontSize: 13, color: 'var(--ink-700)' }}>
                    <div className="mono" style={{ fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--ink-500)', marginBottom: 4 }}>Verzendadres</div>
                    {status.delivery.address.name && <div>{status.delivery.address.name}</div>}
                    <div>{status.delivery.address.street}</div>
                    <div>{status.delivery.address.postalCode} {status.delivery.address.city}</div>
                    {status.delivery.address.country && <div>{status.delivery.address.country}</div>}
                    {Number(status.delivery.cost) > 0 && <div style={{ marginTop: 6, color: 'var(--ink-500)' }}>Verzendkost: €{Number(status.delivery.cost).toFixed(2)}</div>}
                  </div>
                )}
                <p className="mono" style={{ fontSize: 11, color: 'var(--ink-500)', letterSpacing: '0.08em', marginTop: 12 }}>
                  Order: {status.id}
                </p>
                <div style={{ display: 'flex', gap: 8, justifyContent: 'center', marginTop: 24, flexWrap: 'wrap' }}>
                  <a className="btn btn-primary" href={`/api/shop/order/${encodeURIComponent(status.id)}/invoice`} target="_blank" rel="noreferrer">
                    📄 Download factuur (PDF)
                  </a>
                  <button className="btn btn-ghost" onClick={() => navigate('webshop')}>Verder winkelen</button>
                </div>
              </>
            ) : isFailed ? (
              <>
                <div style={{ width: 72, height: 72, borderRadius: '50%', background: 'var(--ink-100)', color: 'var(--ink-700)', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 24px', fontSize: 32, fontWeight: 700 }}>×</div>
                <h2 className="display" style={{ fontSize: 26, margin: '0 0 12px', textTransform: 'none' }}>Betaling niet voltooid</h2>
                {status?.checkoutUrl && status?.paymentStatus === 'open' && (
                  <a className="btn btn-primary" href={status.checkoutUrl}>Opnieuw proberen <Icon.ArrowRight /></a>
                )}
                <button className="btn btn-ghost" style={{ marginLeft: 8 }} onClick={() => navigate('webshop')}>Terug naar webshop</button>
              </>
            ) : (
              <>
                <h2 className="display" style={{ fontSize: 26, margin: '0 0 12px', textTransform: 'none' }}>Betaling in behandeling</h2>
                <p style={{ color: 'var(--ink-500)' }}>Sommige methodes hebben tijd nodig. Je krijgt een mail zodra bevestigd.</p>
              </>
            )}
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// ============================================
// Verlanglijst
// ============================================
function WishlistPage({ navigate, me, wl }) {
  const { Reveal, Icon } = window.LM_UI;
  const [products, setProducts] = React.useState(null);

  React.useEffect(() => {
    window.LM_SHOP.listProducts().then(setProducts).catch(() => setProducts([]));
  }, []);

  if (me && me.authed === false) {
    return (
      <section className="section">
        <div className="container" style={{ maxWidth: 560 }}>
          <Reveal>
            <div className="card" style={{ padding: 40, textAlign: 'center' }}>
              <div style={{ fontSize: 40, marginBottom: 12 }}>♥</div>
              <h1 className="display" style={{ fontSize: 26, margin: '0 0 8px', textTransform: 'none' }}>Jouw verlanglijst</h1>
              <p style={{ color: 'var(--ink-500)', margin: '0 0 20px', fontSize: 14 }}>Meld je aan om producten te bewaren en op elk toestel terug te vinden.</p>
              <button className="btn btn-primary" onClick={() => navigate('webshop/login')}>Aanmelden <Icon.ArrowRight /></button>
            </div>
          </Reveal>
        </div>
      </section>
    );
  }

  const ids = (wl && wl.ids) || [];
  const byId = {};
  (products || []).forEach(p => { byId[p.id] = p; });
  const list = ids.map(id => byId[id]).filter(Boolean);

  return (
    <section className="section">
      <div className="container">
        <Reveal>
          <button className="btn btn-ghost btn-sm" style={{ marginBottom: 16 }} onClick={() => navigate('webshop')}>
            <Icon.ArrowRight size={12} style={{ transform: 'rotate(180deg)' }} /> Verder winkelen
          </button>
          <h1 className="display" style={{ fontSize: 'clamp(32px, 4.5vw, 48px)', margin: '0 0 24px', textTransform: 'none', letterSpacing: '-0.015em' }}>
            Verlanglijst
          </h1>
        </Reveal>

        {products === null ? (
          <div style={{ padding: 60, textAlign: 'center', color: 'var(--ink-500)' }}>Laden…</div>
        ) : list.length === 0 ? (
          <div className="card" style={{ padding: 60, textAlign: 'center' }}>
            <div style={{ fontSize: 40, marginBottom: 12, color: 'var(--ink-300)' }}>♡</div>
            <p style={{ color: 'var(--ink-500)', marginBottom: 16 }}>Je verlanglijst is nog leeg. Tik op het hartje bij een product om het te bewaren.</p>
            <button className="btn btn-primary" onClick={() => navigate('webshop')}>Naar de webshop <Icon.ArrowRight /></button>
          </div>
        ) : (
          <div className="grid grid-3">
            {list.map(p => (
              <ProductCard key={p.id} product={p} onOpen={() => navigate('webshop/product/' + p.id)} wl={wl} />
            ))}
          </div>
        )}
      </div>
    </section>
  );
}

// ============================================
// Cadeaubon kopen (webshop)
// ============================================
// Wordt getoond als de schooleigenaar de cadeaubon-verkoop in de webshop heeft uitgezet.
function GiftCardsDisabledPage({ navigate }) {
  const { Icon } = window.LM_UI;
  return (
    <section className="section" style={{ paddingBottom: 32 }}>
      <div className="container" style={{ maxWidth: 720, textAlign: 'center' }}>
        <button className="btn btn-ghost btn-sm" style={{ marginBottom: 16 }} onClick={() => navigate('webshop')}>
          <Icon.ArrowRight size={12} style={{ transform: 'rotate(180deg)' }} /> Terug naar webshop
        </button>
        <div className="card" style={{ padding: 40 }}>
          <div style={{ fontSize: 48, marginBottom: 12 }}>🎁</div>
          <h2 className="display" style={{ fontSize: 28, margin: '0 0 8px', textTransform: 'none' }}>Cadeaubonnen zijn momenteel niet beschikbaar</h2>
          <p style={{ color: 'var(--ink-500)', margin: 0 }}>Wil je toch een cadeaubon? Neem dan even contact op met de dansschool.</p>
        </div>
      </div>
    </section>
  );
}

function GiftCardPage({ navigate }) {
  const { Reveal, SectionHeader, Icon } = window.LM_UI;
  const PRESETS = [10, 20, 30, 50];
  const [templates, setTemplates] = React.useState([]);
  const [amount, setAmount] = React.useState(20);
  const [customAmount, setCustomAmount] = React.useState('');
  const [templateId, setTemplateId] = React.useState('');
  const [recipient, setRecipient] = React.useState('');
  const [from, setFrom] = React.useState('');
  const [message, setMessage] = React.useState('');
  const [buyerName, setBuyerName] = React.useState('');
  const [buyerEmail, setBuyerEmail] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState('');
  const submittingRef = React.useRef(false);

  React.useEffect(() => {
    window.LM_SHOP.giftTemplates().then(r => {
      const list = r.templates || [];
      setTemplates(list);
      if (list.length) setTemplateId(list[0].id);
    }).catch(() => {});
  }, []);

  const finalAmount = customAmount !== '' ? Number(customAmount) : amount;
  const inputStyle = { padding: '10px 14px', borderRadius: 10, border: '1.5px solid var(--ink-200)', fontSize: 14, fontFamily: 'inherit', background: 'var(--surface)', width: '100%' };
  const fixImg = (u) => (u || '').replace(/^(?!\/|https?:\/\/|data:)/, '/');

  // Welke velden komen op de gekozen template? (recipient/from/message — afgeleid uit
  // de layout van de template; bij geen template of zonder layout: alles tonen.)
  const selectedTpl = templates.find(t => t.id === templateId);
  const tplFields = (selectedTpl && selectedTpl.fields) || { recipient: true, from: true, message: true };
  const showRecipient = tplFields.recipient !== false;
  const showFrom = tplFields.from !== false;
  const showMessage = tplFields.message !== false;

  const submit = async (e) => {
    e.preventDefault();
    if (submittingRef.current) return;
    setError('');
    if (!(finalAmount >= 5 && finalAmount <= 500)) { setError('Kies een bedrag tussen €5 en €500.'); return; }
    if (!buyerName.trim()) { setError('Vul je naam in.'); return; }
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(buyerEmail.trim())) { setError('Vul een geldig e-mailadres in.'); return; }
    submittingRef.current = true; setBusy(true);
    try {
      const r = await window.LM_SHOP.buyGiftCard({
        amount: finalAmount, templateId: templateId || undefined,
        // Enkel meesturen wat de gekozen template ook echt afdrukt.
        recipient: showRecipient ? recipient.trim() : '',
        from:      showFrom      ? from.trim()      : '',
        message:   showMessage   ? message.trim()   : '',
        buyerName: buyerName.trim(), buyerEmail: buyerEmail.trim(),
      });
      if (r.checkoutUrl) window.location.href = r.checkoutUrl;
      else navigate('webshop/gift-return?id=' + r.id);
    } catch (err) {
      setError(err.message || 'Aankoop kon niet gestart worden.');
      submittingRef.current = false; setBusy(false);
    }
  };

  return (
    <div>
      <section className="section" style={{ paddingBottom: 32 }}>
        <div className="container" style={{ maxWidth: 720 }}>
          <button className="btn btn-ghost btn-sm" style={{ marginBottom: 16 }} onClick={() => navigate('webshop')}><Icon.ArrowRight size={12} style={{ transform: 'rotate(180deg)' }} /> Terug naar webshop</button>
          <SectionHeader eyebrow="Cadeaubon" title={<>Geef <span className="grad-text">Dance Studio</span> cadeau</>} subtitle="Kies een bedrag en een ontwerp. Na betaling ontvang je de cadeaubon meteen per e-mail om af te drukken of door te sturen." />
          <form onSubmit={submit} className="card" style={{ padding: 32 }}>
            <label className="label">Bedrag</label>
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 16 }}>
              {PRESETS.map(v => (
                <button key={v} type="button" onClick={() => { setAmount(v); setCustomAmount(''); }} className={`chip ${customAmount === '' && amount === v ? 'active' : ''}`}>€{v}</button>
              ))}
              <input type="number" min="5" max="500" step="1" value={customAmount} onChange={e => setCustomAmount(e.target.value)} placeholder="Ander bedrag (€)" style={{ ...inputStyle, width: 160 }} />
            </div>

            {templates.length > 0 && (
              <>
                <label className="label">Ontwerp</label>
                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: 10, marginBottom: 18 }}>
                  {templates.map(t => (
                    <button key={t.id} type="button" onClick={() => setTemplateId(t.id)} style={{
                      textAlign: 'left', cursor: 'pointer', borderRadius: 12, padding: 0, overflow: 'hidden',
                      border: templateId === t.id ? '2px solid var(--blue-500)' : '1.5px solid var(--ink-200)', background: 'var(--surface)',
                    }}>
                      <div style={{ height: 54, background: t.backgroundImage ? `center/cover no-repeat url("${fixImg(t.backgroundImage)}")` : (t.headerColor || '#0e1a26') }} />
                      <div style={{ padding: '8px 10px' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                          <span style={{ width: 12, height: 12, borderRadius: 3, background: t.accentColor || '#3fa89e', display: 'inline-block', flexShrink: 0 }} />
                          <strong style={{ fontSize: 13 }}>{t.name}</strong>
                        </div>
                        {t.intro && <div style={{ fontSize: 11, color: 'var(--ink-500)', marginTop: 2, lineHeight: 1.3 }}>{t.intro}</div>}
                      </div>
                    </button>
                  ))}
                </div>
              </>
            )}

            {(showRecipient || showFrom) && (
              (showRecipient && showFrom) ? (
                <div className="grid grid-2" style={{ marginBottom: 12 }}>
                  <div><label className="label">Voor (naam ontvanger)</label><input value={recipient} onChange={e => setRecipient(e.target.value)} placeholder="bv. Emma" style={inputStyle} /></div>
                  <div><label className="label">Van</label><input value={from} onChange={e => setFrom(e.target.value)} placeholder="bv. Oma & Opa" style={inputStyle} /></div>
                </div>
              ) : (
                <div style={{ marginBottom: 12 }}>
                  {showRecipient && <><label className="label">Voor (naam ontvanger)</label><input value={recipient} onChange={e => setRecipient(e.target.value)} placeholder="bv. Emma" style={inputStyle} /></>}
                  {showFrom && <><label className="label">Van</label><input value={from} onChange={e => setFrom(e.target.value)} placeholder="bv. Oma & Opa" style={inputStyle} /></>}
                </div>
              )
            )}
            {showMessage && (
              <div style={{ marginBottom: 16 }}><label className="label">Persoonlijke boodschap (optioneel)</label><input value={message} onChange={e => setMessage(e.target.value)} maxLength={200} placeholder="bv. Veel dansplezier!" style={inputStyle} /></div>
            )}

            <div className="grid grid-2" style={{ marginBottom: 16 }}>
              <div><label className="label">Jouw naam *</label><input value={buyerName} onChange={e => setBuyerName(e.target.value)} required style={inputStyle} /></div>
              <div><label className="label">Jouw e-mail *</label><input type="email" value={buyerEmail} onChange={e => setBuyerEmail(e.target.value)} required placeholder="We sturen de cadeaubon hierheen" style={inputStyle} /></div>
            </div>

            {error && <div style={{ background: '#fde8e8', color: '#9b1c1c', padding: 12, borderRadius: 8, marginBottom: 12, fontSize: 14 }}>{error}</div>}

            <button type="submit" className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} disabled={busy}>
              {busy ? 'Bezig…' : <>Cadeaubon kopen — €{(Number(finalAmount) || 0).toFixed(2)} <Icon.ArrowRight /></>}
            </button>
            <p style={{ fontSize: 12, color: 'var(--ink-500)', marginTop: 10, textAlign: 'center' }}>Veilig betalen via Mollie. De cadeaubon is 2 jaar geldig en inwisselbaar in de webshop of in de winkel.</p>
          </form>
        </div>
      </section>
    </div>
  );
}

// ============================================
// Cadeaubon return (na Mollie checkout)
// ============================================
function GiftReturnPage({ navigate }) {
  const { Reveal, Icon } = window.LM_UI;
  const getId = () => { const m = (window.location.hash || '').match(/[?&]id=([^&]+)/); return m ? decodeURIComponent(m[1]) : ''; };
  const [id] = React.useState(getId);
  const [status, setStatus] = React.useState(null);
  const [polling, setPolling] = React.useState(true);
  const [err, setErr] = React.useState('');

  React.useEffect(() => {
    if (!id) { setErr('Geen referentie gevonden.'); setPolling(false); return; }
    let cancelled = false, timer = null, tries = 0;
    const tick = async () => {
      tries++;
      try {
        const s = await window.LM_SHOP.giftCardStatus(id);
        if (cancelled) return;
        setStatus(s);
        if (s.status === 'paid' || ['failed', 'canceled', 'expired'].includes(s.paymentStatus) || tries >= 12) { setPolling(false); return; }
      } catch { if (cancelled) return; setErr('Status kon niet opgehaald worden.'); setPolling(false); return; }
      timer = setTimeout(tick, 2500);
    };
    tick();
    return () => { cancelled = true; if (timer) clearTimeout(timer); };
  }, [id]);

  const isPaid = status?.status === 'paid';
  const isFailed = ['failed', 'canceled', 'expired'].includes(status?.paymentStatus);

  return (
    <section className="section" style={{ minHeight: '60vh' }}>
      <div className="container" style={{ maxWidth: 680 }}>
        <Reveal>
          <div className="card" style={{ padding: '48px 40px', textAlign: 'center' }}>
            {err ? <h2 className="display" style={{ fontSize: 28, margin: '0 0 12px', textTransform: 'none' }}>{err}</h2>
            : polling ? (<>
                <div style={{ width: 56, height: 56, borderRadius: '50%', border: '4px solid var(--ink-100)', borderTopColor: 'var(--blue-500)', margin: '0 auto 20px', animation: 'spin 1s linear infinite' }} />
                <h2 className="display" style={{ fontSize: 26, margin: '0 0 12px', textTransform: 'none' }}>We bevestigen je betaling…</h2>
                <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
              </>)
            : isPaid ? (<>
                <div style={{ width: 72, height: 72, borderRadius: '50%', background: 'var(--gradient)', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', margin: '0 auto 24px' }}><Icon.Check size={32} /></div>
                <h2 className="display" style={{ fontSize: 30, margin: '0 0 12px', textTransform: 'none' }}>Je cadeaubon is klaar! 🎁</h2>
                <p style={{ fontSize: 15, color: 'var(--ink-500)' }}>Je betaling van <strong>€{(Number(status.amount) || 0).toFixed(2)}</strong> is bevestigd. We hebben de cadeaubon ook naar je e-mail gestuurd.</p>
                <div style={{ margin: '20px auto', maxWidth: 320, padding: '14px 18px', border: '2px dashed var(--ink-200)', borderRadius: 12 }}>
                  <div className="mono" style={{ fontSize: 12, color: 'var(--ink-500)' }}>CODE</div>
                  <div style={{ fontSize: 22, fontWeight: 800, letterSpacing: '0.04em' }}>{status.code}</div>
                </div>
                {status.pdf && <a className="btn btn-primary" href={status.pdf} target="_blank" rel="noreferrer">📄 Download cadeaubon (PDF)</a>}
                <div style={{ marginTop: 16 }}><button className="btn btn-ghost btn-sm" onClick={() => navigate('webshop')}>← Terug naar de webshop</button></div>
              </>)
            : isFailed ? (<>
                <h2 className="display" style={{ fontSize: 28, margin: '0 0 12px', textTransform: 'none' }}>Betaling niet voltooid</h2>
                <p style={{ color: 'var(--ink-500)' }}>Er is geen geld afgeschreven. Je kan het opnieuw proberen.</p>
                {status?.checkoutUrl && <a className="btn btn-primary" href={status.checkoutUrl}>Opnieuw proberen <Icon.ArrowRight /></a>}
                <div style={{ marginTop: 16 }}><button className="btn btn-ghost btn-sm" onClick={() => navigate('webshop/cadeaubon')}>← Terug</button></div>
              </>)
            : (<>
                <h2 className="display" style={{ fontSize: 26, margin: '0 0 12px', textTransform: 'none' }}>Betaling in verwerking…</h2>
                <p style={{ color: 'var(--ink-500)' }}>Dit kan even duren. Je ontvangt de cadeaubon per e-mail zodra de betaling bevestigd is.</p>
                {status?.checkoutUrl && <a className="btn btn-primary" href={status.checkoutUrl}>Betaling afronden <Icon.ArrowRight /></a>}
              </>)}
          </div>
        </Reveal>
      </div>
    </section>
  );
}

window.LM_PAGES = window.LM_PAGES || {};
window.LM_PAGES.Webshop = WebshopPage;
