// Pricing page for batteori.no — checkout funnel
const I = window.Icons;
const { useState, useEffect, useRef } = React;

// ---- Config ----
const ENABLE_VIPPS = false; // flip when Vipps eHandel approval lands
const SUPABASE_URL = 'https://eqihrzxhrrqqpfgteqye.supabase.co';
const STRIPE_CHECKOUT_FN = `${SUPABASE_URL}/functions/v1/stripe-checkout`;
const CAPTURE_EMAIL_FN = `${SUPABASE_URL}/functions/v1/capture-email`;
const BASE_URL = window.location.origin + window.location.pathname.replace(/\/[^/]*$/, '');
const PLAN_SLUG = 'batforer';
const CURRENCY = 'NOK';

// Båtteori tiers — id is plan_tiers.slug; backend resolves stripe_price_id
// from (planSlug, tierSlug). Prices come from window.PRICES (shared/prices.js,
// loaded synchronously by Priser before this file). Stripe price IDs
// live in supabase/migrations/20260515000000_seed_stripe_price_ids_live.sql.
const TIERS = [
  {
    id: '3d', label: '3 dager', days: 3, price: window.PRICES['batforer']['3d'],
    tagline: 'Siste rep før prøven.',
    features: ['Full tilgang i 3 dager', 'Alle 1 800+ spørsmål', 'Prøve-eksamener på tid'],
  },
  {
    id: '14d', label: '14 dager', days: 14, price: window.PRICES['batforer']['14d'], popular: true,
    tagline: 'Vår anbefaling — rom til å mestre alt.',
    features: ['Full tilgang i 14 dager', 'Smart repetisjon tilpasset deg', 'Lanterne- og karttrener', 'Offline-modus'],
  },
  {
    id: '30d', label: '30 dager', days: 30, price: window.PRICES['batforer']['30d'],
    tagline: 'Best om du starter fra bunnen.',
    features: ['Full tilgang i 30 dager', 'Ubegrensede prøve-eksamener', 'Prioritert support', 'Fremdrift synkes mellom enheter', 'Bestått-garanti'],
  },
];

// 2026-08-16 (Priser-playbook, eiergodkjent): appen skiller ikke funksjoner
// per pakke — tilgang gis per plan. Alle pakker viser derfor samme linjer;
// pakker som lovet garanti beholder den linjen. TIERS[].features over er
// ikke lenger i bruk på siden (beholdt for LP-paritet).
const COMMON_FEATURES = ['Alle 1 800+ spørsmål','Prøve-eksamener på tid','Smart repetisjon tilpasset deg','Lanterne- og karttrener'];
const EXTRA_FEATURES = {'30d':['Bestått-garanti']};
const featuresFor = (t) => [...COMMON_FEATURES, ...(EXTRA_FEATURES[t.id] || [])];

// ---- DataLayer helper (GTM-friendly) ----
// Consent gating is handled downstream: consent-config.js sets Google Consent
// Mode v2 default-denied before GTM loads, and GTM tags use consent triggers
// (analytics_storage / ad_storage) to decide whether to fire. Pushing to
// dataLayer here is safe — it just queues events for GTM to filter.
function pushDataLayer(event, payload = {}) {
  if (typeof window === 'undefined') return;
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ event, ...payload });
}

// ---- Attribution capture ----
// First-touch UTM + landing + referrer, sessionStorage-cached. Strips query
// string off landing_page to avoid PII leaks into the stored URL.
// Fire-and-forget: store a valid email the moment it's entered (blur), even if
// the visitor never proceeds to payment. Store-only — no email is ever sent to
// these captures. Guarded so a given address posts at most once per page load.
let _capturedEmail = null;
function captureEmailLead(email, planSlug, tierSlug, attribution) {
  if (!/^\S+@\S+\.\S+$/.test(email || '')) return;
  const clean = email.toLowerCase().trim();
  if (clean === _capturedEmail) return;
  _capturedEmail = clean;
  try {
    fetch(CAPTURE_EMAIL_FN, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: clean, planSlug, tierSlug, attribution }),
      keepalive: true,
    }).catch(() => {});
  } catch (_) { /* ignore */ }
}

function captureAttribution() {
  if (typeof window === 'undefined') return {};
  const STORAGE_KEY = 'th_attr_v1';
  try {
    const existing = sessionStorage.getItem(STORAGE_KEY);
    if (existing) {
      const cached = JSON.parse(existing);
      // Referral visits count as medium=referral unless explicitly tagged.
      if (!cached.utm_medium && readStoredRef()) cached.utm_medium = 'referral';
      // Click-IDs fill in where missing — an ad-click must not be swallowed
      // by a cached organic first-touch (CAPI needs them to match the click).
      const q = new URLSearchParams(window.location.search);
      let dirty = false;
      for (const k of ['gclid', 'gbraid', 'wbraid', 'fbclid', 'ttclid']) {
        if (!cached[k] && q.get(k)) { cached[k] = q.get(k); dirty = true; }
      }
      if (dirty) { try { sessionStorage.setItem(STORAGE_KEY, JSON.stringify(cached)); } catch (_) {} }
      return cached;
    }
  } catch (_) {}
  const params = new URLSearchParams(window.location.search);
  const attr = {
    utm_source:   params.get('utm_source')   || null,
    utm_medium:   params.get('utm_medium')   || null,
    utm_campaign: params.get('utm_campaign') || null,
    utm_term:     params.get('utm_term')     || null,
    utm_content:  params.get('utm_content')  || null,
    gclid:        params.get('gclid')        || null,
    gbraid:       params.get('gbraid')       || null,
    wbraid:       params.get('wbraid')       || null,
    fbclid:       params.get('fbclid')       || null,
    ttclid:       params.get('ttclid')       || null,
    landing_page: window.location.href.split('?')[0],
    referrer:     document.referrer || null,
  };
  // Referral visits count as medium=referral unless explicitly tagged.
  if (!attr.utm_medium && readStoredRef()) attr.utm_medium = 'referral';
  try { sessionStorage.setItem(STORAGE_KEY, JSON.stringify(attr)); } catch (_) {}
  return attr;
}

// ---- Referral capture (v2) ----
// docs/REFERRAL_V2_PLAN.md WP-3. ?ref= (or ?vervekode=) carries the personal
// code across domains — the /r/{kode}-landing on teorihuset.no appends it to
// outbound LP links, since localStorage never crosses origins. Mirrors the
// th_attr_v1 pattern above, but in localStorage (the code must survive the
// tab) with a 90-day expiry enforced on read.
const REF_STORAGE_KEY = 'th_ref_v1';
const REF_TTL_MS = 90 * 24 * 60 * 60 * 1000;
// TH-XXXXXX (v2) or legacy-ish codes: uppercase alnum + dash, 4-16 chars.
const REF_CODE_RE = /^[A-Z0-9][A-Z0-9-]{2,14}[A-Z0-9]$/;

function normalizeRefCode(raw) {
  if (!raw) return null;
  const code = String(raw).trim().toUpperCase();
  return REF_CODE_RE.test(code) ? code : null;
}

function readStoredRef() {
  try {
    const raw = localStorage.getItem(REF_STORAGE_KEY);
    if (!raw) return null;
    const stored = JSON.parse(raw);
    const code = normalizeRefCode(stored && stored.code);
    const fresh = stored && typeof stored.ts === 'number' && (Date.now() - stored.ts) < REF_TTL_MS;
    if (!code || !fresh) {
      localStorage.removeItem(REF_STORAGE_KEY);
      return null;
    }
    return code;
  } catch (_) { return null; }
}

function captureReferral() {
  if (typeof window === 'undefined') return { code: null, fromUrl: false };
  const params = new URLSearchParams(window.location.search);
  const fromUrl = normalizeRefCode(params.get('ref') || params.get('vervekode'));
  if (fromUrl) {
    try { localStorage.setItem(REF_STORAGE_KEY, JSON.stringify({ code: fromUrl, ts: Date.now() })); } catch (_) {}
    pushDataLayer('referral_link_visit', { referral_code: fromUrl, plan_slug: PLAN_SLUG });
    return { code: fromUrl, fromUrl: true };
  }
  return { code: readStoredRef(), fromUrl: false };
}

// Run at load so the code persists even if the visitor doesn't buy this visit.
const CAPTURED_REF = captureReferral();

function tierToItem(t) {
  return {
    item_id: `${PLAN_SLUG}_${t.id}`,
    item_name: `${t.label} · ${t.days} dager`,
    item_category: PLAN_SLUG,
    price: t.price,
    quantity: 1,
  };
}

// ---- Header ----
function PriserNav() {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', onScroll);
    return () => window.removeEventListener('scroll', onScroll);
  }, []);
  return (
    <header className={`nav ${scrolled ? 'scrolled' : ''}`}>
      <div className="wrap nav-inner">
        <a href="index.html" className="brand" aria-label="Teorihuset">
          <span className="brand-badge"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" aria-hidden="true" focusable="false" viewBox="0 0 512 512"><defs><clipPath id="logoBadgeClip"><rect width="512" height="512" rx="115"/></clipPath></defs><g clipPath="url(#logoBadgeClip)"><rect width="512" height="512" fill="#F0AC1E"/><circle cx="90" cy="60" r="130" fill="#F3B935"/><circle cx="440" cy="470" r="120" fill="#EDA414"/><path d="M256 148 L400 219 L256 290 L112 219 Z" fill="#1B2B40"/><path d="M181 234 v70 c0 29 33 45 75 45 s75 -16 75 -45 v-70" fill="none" stroke="#1B2B40" strokeWidth="34" strokeLinecap="round"/><circle cx="256" cy="215" r="14" fill="#FAECD8"/><path d="M413 213 v112" stroke="#FAECD8" strokeWidth="17" strokeLinecap="round"/><circle cx="413" cy="333" r="20" fill="#FAECD8"/></g></svg></span>
          <span>Teorihuset</span>
        </a>
        <div className="nav-cta">
          <a href="index.html" className="btn btn-ghost btn-sm" style={{display:'inline-flex'}}>← Tilbake</a>
        </div>
      </div>
    </header>
  );
}

// ---- Tier cards ----
function TierCards({ selectedId, onSelect }) {
  return (
    <div className="pr-tiers" role="radiogroup" aria-label="Velg plan">
      {TIERS.map(t => {
        const isSel = t.id === selectedId;
        return (
          <button
            key={t.id}
            type="button"
            role="radio"
            aria-checked={isSel}
            className={`pr-tier ${isSel ? 'pr-tier--selected' : ''} ${t.popular ? 'pr-tier--popular' : ''}`}
            onClick={() => onSelect(t.id)}
          >
            {t.popular && <span className="pr-tier-ribbon">Mest valgt</span>}
            <div className="pr-tier-head">
              <span className="pr-tier-meta">{t.days} dager · {t.label}</span>
              <span className="pr-tier-radio" aria-hidden="true">
                {isSel && <I.Check size={14} />}
              </span>
            </div>
            <div className="pr-tier-price">
              <span className="pr-tier-amount">{t.price}</span>
              <span className="pr-tier-currency">kr</span>
            </div>
            <div style={{ fontSize: '12px', color: 'var(--ink-3)', marginTop: '-4px', marginBottom: '10px', letterSpacing: '0.01em' }}>Inkl. mva.</div>
            <p className="pr-tier-tagline">{t.tagline}</p>
            <ul className="pr-tier-features">
              {featuresFor(t).map((f, i) => (
                <li key={i}><I.Check size={14} /> {f}</li>
              ))}
            </ul>
          </button>
        );
      })}
    </div>
  );
}

// ---- Referral code field (v2) ----
// Collapsed link by default; auto-expanded + prefilled when a code was
// captured from ?ref=/th_ref_v1. Deliberately promises no amount client-side
// for unvalidated codes — stripe-checkout validates and shows the discount.
function ReferralField({ code, setCode }) {
  const [open, setOpen] = useState(!!CAPTURED_REF.code);
  if (!open) {
    return (
      <button type="button" className="pr-ref-toggle" onClick={() => setOpen(true)}>
        Har du en vervekode?
      </button>
    );
  }
  const isCaptured = !!code && code === CAPTURED_REF.code;
  return (
    <label className="pr-field">
      <span className="pr-field-label">Vervekode</span>
      <input
        type="text"
        autoComplete="off"
        autoCapitalize="characters"
        spellCheck="false"
        placeholder="TH-XXXXXX"
        value={code}
        onChange={e => setCode(e.target.value.toUpperCase())}
        className="pr-field-input"
      />
      <span className="pr-field-hint">
        {isCaptured ? 'Vervekode registrert. Rabatten vises i betalingsvinduet.' : 'Rabatten vises i betalingsvinduet.'}
      </span>
    </label>
  );
}

// ---- Checkout panel ----
function CheckoutPanel({ tier, email, setEmail, refCode, setRefCode, onPay, loading, error }) {
  // E-postporten fjernet 2026-08-01 (traktanalysen pri 1, eiergodkjent):
  // Stripe samler e-post i betalingsvinduet; feltet under er valgfritt og
  // finnes kun for checkout_leads-fangsten (onBlur) + forhåndsutfylling.
  const canPay = !loading;
  return (
    <div className="pr-checkout" id="checkout">
      <div className="pr-checkout-summary">
        <span className="pr-checkout-label">Du betaler for</span>
        <div className="pr-checkout-row">
          <strong>Teorihuset · {tier.days} dager — {tier.label}</strong>
          <span className="pr-checkout-amount">{tier.price} kr</span>
        </div>
        <span className="pr-checkout-sub">Engangsbeløp. Ingen abonnement. Tilgang umiddelbart.</span>
      </div>

      <div style={{margin:'0 0 14px', padding:'11px 13px', borderRadius:10, background:'rgba(0,0,0,0.035)', border:'1px solid rgba(0,0,0,0.09)', fontSize:12.5, lineHeight:1.5}}>
        <strong>📱 Appen «Teorihuset» er ute — kjøpet gjelder der også.</strong>{' '}
        <span style={{color:'var(--ink-3)'}}>Kjøpet gir deg full tilgang på web med én gang. Last ned appen fra App Store eller Google Play og logg inn med samme konto — uten ekstra kostnad.</span>
      </div>

      <label className="pr-field">
        <span className="pr-field-label">E-post (valgfritt)</span>
        <input
          type="email"
          autoComplete="email"
          inputMode="email"
          placeholder="navn@eksempel.no"
          value={email}
          onChange={e => setEmail(e.target.value)}
          onBlur={() => captureEmailLead(email, PLAN_SLUG, tier.id, captureAttribution())}
          className="pr-field-input"
        />
        <span className="pr-field-hint">Vi sender innloggingslenke og kvittering hit — du kan også fylle den inn i betalingsvinduet.</span>
      </label>

      <ReferralField code={refCode} setCode={setRefCode} />

      {error && <div className="pr-error">{error}</div>}

      <div className="pr-pay-row">
        {ENABLE_VIPPS && (
          <button type="button" className="btn btn-primary btn-lg pr-pay-vipps" onClick={onPay} disabled={!canPay}>
            Betal med Vipps
          </button>
        )}
        <button
          type="button"
          className="btn btn-primary btn-lg pr-pay-stripe"
          onClick={onPay}
          disabled={!canPay}
        >
          {loading ? 'Sender til betaling…' : <>Betal med kort <I.Arrow size={16} /></>}
        </button>
      </div>

      <div className="pr-checkout-trust">
        <span><I.Lock size={13} /> Sikker betaling via Stripe</span>
        <span><I.Shield size={13} /> Bestått-garanti inkludert</span>
      </div>
      <div style={{marginTop:10, fontSize:12, lineHeight:1.5, color:'var(--ink-3)', textAlign:'center'}}>
        Ved å gå videre godtar du <a href="Vilkar" style={{color:'inherit', textDecoration:'underline'}}>Vilkår</a> og <a href="Personvern" style={{color:'inherit', textDecoration:'underline'}}>Personvern</a>.
      </div>
    </div>
  );
}

// ---- Mobil: én skjerm (2026-08-16, Priser-playbook) ----
function SummaryCard({ tier, onPay, loading, error }) {
  return (
    <div className="pr-summary" id="checkout-mobil">
      <span className="pr-checkout-label">Du betaler for</span>
      <div className="pr-checkout-row">
        <strong>{tier.days} dager{tier.label !== `${tier.days} dager` ? ` — ${tier.label}` : ''}</strong>
        <span className="pr-checkout-amount">{tier.price} kr</span>
      </div>
      <span className="pr-checkout-sub">Engangsbeløp. Ingen abonnement. Tilgang umiddelbart.</span>
      <ul className="pr-tier-features pr-summary-features">
        {featuresFor(tier).map((f, i) => (<li key={i}><I.Check size={14} /> <span>{f}</span></li>))}
      </ul>
      {error && <div className="pr-error" style={{ marginTop: 14 }}>{error}</div>}
      <button type="button" className="btn btn-primary btn-lg pr-summary-pay" onClick={onPay} disabled={loading}>
        {loading ? 'Sender til betaling…' : <>Betal {tier.price} kr <I.Arrow size={16} /></>}
      </button>
      <div className="pr-checkout-trust">
        <span><I.Lock size={13} /> Sikker betaling</span>
        <span>Kort · Apple Pay · Google Pay</span>
        <span><I.Shield size={13} /> Bestått-garanti inkludert</span>
      </div>
      <div className="pr-terms">
        Ved å gå videre godtar du <a href="Vilkar">Vilkår</a> og <a href="Personvern">Personvern</a>.
      </div>
    </div>
  );
}

function TierSwitch({ selectedId, onSelect }) {
  return (
    <div className="pr-switch-wrap">
      <span className="pr-switch-label" id="pr-switch-label">Hvor lang tid trenger du?</span>
      <div className="pr-switch" role="radiogroup" aria-labelledby="pr-switch-label">
        {TIERS.map(t => {
          const sel = t.id === selectedId;
          const small = t.label !== `${t.days} dager` ? t.label : ((EXTRA_FEATURES[t.id] || []).length ? 'Bestått-garanti' : '');
          return (
            <button key={t.id} type="button" role="radio" aria-checked={sel} className={'pr-seg' + (sel ? ' pr-seg--selected' : '')} onClick={() => onSelect(t.id)}>
              {t.popular && <span className="pr-seg-ribbon">Mest valgt</span>}
              <span className="pr-seg-days">{t.days} dager</span>
              <span className="pr-seg-price">{t.price} kr</span>
              <span className="pr-seg-name">{small}</span>
            </button>
          );
        })}
      </div>
      <p className="pr-switch-hint">Samme innhold i alle tre. Forskjellen er hvor lenge du har tilgang.</p>
      <div className="pr-appnote">
        <strong>📱 Appen «Teorihuset» er ute — kjøpet gjelder der også.</strong>{' '}
        <span style={{color:'var(--ink-3, var(--muted))'}}>Kjøpet gir deg full tilgang på web med én gang. Last ned appen fra App Store eller Google Play og logg inn med samme konto — uten ekstra kostnad.</span>
      </div>
    </div>
  );
}

function MobileReferral({ refCode, setRefCode }) {
  return (
    <div className="pr-mobile-ref">
      <ReferralField code={refCode} setCode={setRefCode} />
    </div>
  );
}

// ---- Mobile-only summary bar (Stripe-style) ----
function MobileSummaryBar({ tier, visible, onPay, loading }) {
  return (
    <div className={`pr-stickybar ${visible ? 'is-visible' : ''}`} aria-hidden={!visible}>
      <div className="pr-stickybar-inner">
        <div className="pr-stickybar-meta">
          <span className="pr-stickybar-label">{tier.days} dager · {tier.label}</span>
          <span className="pr-stickybar-amount">{tier.price} kr</span>
        </div>
        <button type="button" className="btn btn-primary btn-sm" onClick={onPay} disabled={loading}>{loading ? 'Sender…' : 'Betal →'}</button>
      </div>
    </div>
  );
}

// ---- Trust band ----
function PriserTrust() {
  return (
    <div className="pr-trustband">
      <div className="pr-trust-chip"><I.Shield size={14} /> Bestått-garanti</div>
      <div className="pr-trust-chip"><I.Check size={14} /> Engangsbeløp — ikke abonnement</div>
      <div className="pr-trust-chip"><I.Phone size={14} /> Norsk support</div>
      <a href="Sample" className="pr-trust-sample">Prøv en spørsmålsrunde →</a>
    </div>
  );
}

// ---- Mini-FAQ (3 pricing-related Qs) ----
function PriserFAQ() {
  const items = [
    { q: 'Hva dekker bestått-garantien?', a: 'Hvis du gjennomfører det anbefalte opplegget i appen og likevel stryker på båtførerprøven, refunderer vi hele beløpet. Du sender oss kvittering på prøveresultatet — så ordner vi resten.' },
    { q: 'Hvordan får jeg tilgang etter betaling?', a: 'Du sendes til en kvitteringsside og får en innloggingslenke på e-post umiddelbart. Klikk lenken og du er inne i appen.' },
    { q: 'Kan jeg bytte plan etterpå?', a: 'Ja. Trenger du mer tid, kan du oppgradere til en lengre plan. Kontakt support og vi ordner mellomlegget.' },
  ];
  const [open, setOpen] = useState(0);
  return (
    <div className="pr-faq">
      <span className="eyebrow">Spørsmål om betaling</span>
      <div className="faq-list" style={{marginTop: 14}}>
        {items.map((it, i) => (
          <div className={`faq-item ${open === i ? 'open' : ''}`} key={i}>
            <button type="button" className="faq-q" onClick={() => setOpen(open === i ? -1 : i)} aria-expanded={open === i}>
              <span>{it.q}</span>
              <span className="faq-icon"><I.Plus size={16} /></span>
            </button>
            <div className="faq-a"><div className="faq-a-inner">{it.a}</div></div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ---- Footer ----
function PriserFooter() {
  return (
    <footer>
      <div className="wrap">
        <div className="footer-grid">
          <div className="footer-brand">
            <div className="brand">
              <span className="brand-mark"><I.Anchor size={20} /></span>
              <span>Teorihuset</span>
            </div>
            <p>Norsk trening til båtførerbeviset. Laget i samarbeid med instruktører langs kysten.</p>
          </div>
          <div className="footer-col">
            <h4>Produkt</h4>
            <ul>
              <li><a href="index.html#veien">Veien til bestått</a></li>
              <li><a href="index.html#pensum">Pensum</a></li>
              <li><a href="Priser">Priser</a></li>
              <li><a href="index.html#faq">FAQ</a></li>
            </ul>
          </div>
          <div className="footer-col">
            <h4>Selskap</h4>
            <ul>
              <li><a href="#">Om oss</a></li>
              <li><a href="#">Kontakt</a></li>
              <li><a href="Personvern">Personvern</a></li>
              <li><a href="Vilkar">Vilkår</a></li>
              <li><button type="button" className="footer-col-btn" onClick={() => window.openConsentPreferences && window.openConsentPreferences()}>Endre samtykke</button></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 Teorihuset — Made in Norway</span>
          <span>Made in Norway</span>
        </div>
      </div>
    </footer>
  );
}

// ---- App root ----
function PriserApp() {
  const params = new URLSearchParams(window.location.search);
  const fromParam = params.get('plan');
  const initialId =
    (fromParam && TIERS.some(t => t.id === fromParam)) ? fromParam :
    (TIERS.find(t => t.popular)?.id) || TIERS[0].id;

  const [selectedId, setSelectedId] = useState(initialId);
  const [email, setEmail] = useState('');
  const [refCode, setRefCode] = useState(CAPTURED_REF.code || '');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [stickyVisible, setStickyVisible] = useState(false);

  const tier = TIERS.find(t => t.id === selectedId) || TIERS[0];
  const tiersSentinelRef = useRef(null);

  // Fire view_item_list on mount
  useEffect(() => {
    pushDataLayer('view_item_list', {
      ecommerce: {
        item_list_name: `${PLAN_SLUG}_pricing`,
        items: TIERS.map(tierToItem),
      },
    });
  }, []);

  // Show mobile sticky bar once tier cards scroll out of view
  useEffect(() => {
    if (!tiersSentinelRef.current) return;
    const observer = new IntersectionObserver(
      // Klistrebaren først når betalknappen (sentinel under SummaryCard) har
      // scrollet UT AV SYNE OPPOVER — aldri to priser på første skjerm.
      ([entry]) => setStickyVisible(!entry.isIntersecting && entry.boundingClientRect.top < 0),
      { rootMargin: '0px' },
    );
    observer.observe(tiersSentinelRef.current);
    return () => observer.disconnect();
  }, []);

  const handleSelect = (id) => {
    if (id === selectedId) return;
    setSelectedId(id);
    setError(null);
    const t = TIERS.find(x => x.id === id);
    pushDataLayer('select_item', {
      ecommerce: {
        item_list_name: `${PLAN_SLUG}_pricing`,
        items: [tierToItem(t)],
      },
    });
  };

  const handlePay = async () => {
    setError(null);

    pushDataLayer('begin_checkout', {
      ecommerce: {
        currency: CURRENCY,
        value: tier.price,
        items: [tierToItem(tier)],
      },
    });

    // Referral: typed code wins; falls back to the stored ?ref= capture.
    // Backend validates and responds { url, referralApplied } — invalid
    // codes never block checkout, they just skip the discount.
    const referralCode = normalizeRefCode(refCode) || readStoredRef() || undefined;
    if (referralCode) {
      pushDataLayer('referral_code_applied', { referral_code: referralCode, plan_slug: PLAN_SLUG });
    }

    setLoading(true);
    try {
      const response = await fetch(STRIPE_CHECKOUT_FN, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          planSlug: PLAN_SLUG,
          tierSlug: tier.id,
          customer_email: /^\S+@\S+\.\S+$/.test(email) ? email : undefined,
          referralCode,
          attribution: captureAttribution(),
          success_url: `${BASE_URL}/Takk?session_id={CHECKOUT_SESSION_ID}&plan=${tier.id}`,
          cancel_url: `${BASE_URL}/Priser?retry=1&plan=${tier.id}`,
        }),
      });
      const data = await response.json();
      if (!response.ok || !data.url) {
        throw new Error(data.error || 'Kunne ikke starte betaling. Prøv igjen.');
      }
      window.location.href = data.url;
    } catch (e) {
      console.error('Checkout error', e);
      setError(e.message || 'Noe gikk galt. Prøv igjen.');
      setLoading(false);
    }
  };

  return (
    <>
      <PriserNav />
      <main>
        <section className="pr-section pr-section--hero">
          <div className="wrap">
            <div className="pr-hero">
              <h1>Alt du trenger til båtførerprøven.</h1>
              <p className="lead">Øv som på den ekte prøven — og se når du er klar.</p>
            </div>
          </div>
        </section>

        <section className="pr-section pr-section--mobile-first pr-mobile-only">


          <div className="wrap">


            <SummaryCard tier={tier} onPay={handlePay} loading={loading} error={error} />


            <div ref={tiersSentinelRef} className="pr-tiers-sentinel" aria-hidden="true" />


            <TierSwitch selectedId={selectedId} onSelect={handleSelect} />


          </div>


        </section>



        <section className="pr-section pr-desktop-only">


          <div className="wrap">


            <TierCards selectedId={selectedId} onSelect={handleSelect} />


          </div>


        </section>



        <section className="pr-section pr-desktop-only">
          <div className="wrap pr-narrow">
            <CheckoutPanel
              tier={tier}
              email={email}
              setEmail={setEmail}
              refCode={refCode}
              setRefCode={setRefCode}
              onPay={handlePay}
              loading={loading}
              error={error}
            />
          </div>
        </section>

        <section className="pr-section">
          <div className="wrap pr-narrow">
            <PriserTrust />
          </div>
        </section>

        <section className="pr-section pr-section--faq">
          <div className="wrap pr-narrow">
            <PriserFAQ />
          </div>


        </section>



        <section className="pr-section pr-mobile-only">
          <div className="wrap pr-narrow">
            <MobileReferral refCode={refCode} setRefCode={setRefCode} />
          </div>


        </section>
      </main>
      <MobileSummaryBar tier={tier} visible={stickyVisible} onPay={handlePay} loading={loading} />
      <PriserFooter />
    </>
  );
}

window.PriserApp = PriserApp;
