/* teorihuset.no — homepage / app-visitor conversion flow
 *
 * Job: convert a warm-but-confused visitor sent here from the mobile app's
 * "ingen aktiv tilgang" screen. Spec: docs/TEORIHUSET_LP_BRIEF.md.
 *
 * URL contract from the app:  /?from=app&plan=<slug>&e=<url-encoded-email>
 *   - from=app : switch to the personalised "app visitor" framing
 *   - plan     : the exam the user studies in the app (plan slug)
 *   - e        : the email the user is signed into the app with
 *
 * Email continuity is the make-or-break: access is keyed by email
 * server-side (stripe-webhook). If `e` is present we pre-fill AND LOCK the
 * checkout email so the purchase attaches to the same account. Params are
 * scrubbed from the URL on load and `e` is never sent to analytics.
 *
 * Checkout reuses the shared, plan-agnostic stripe-checkout edge function
 * (resolves price from planSlug + tierSlug). No payment code here.
 */

const I = window.Icons;
const { useState, useEffect, useRef, useMemo } = React;

// ---- Config ----
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;
const CURRENCY = 'NOK';

// Plan + tier catalog. Prices come from window.PRICES (see
// frontend/websites/_shared/prices.js) — that file is the single frontend
// source of truth and is loaded synchronously before this script. Labels,
// taglines and `popular` flags stay local because they're editorial copy
// tuned per surface. Tier slugs must match the backend (planSlug+tierSlug
// → stripe price) — the same slugs are the keys into window.PRICES[slug].
const P = window.PRICES;
const STD_TIERS = (slug) => [
  { slug: '3d',  label: '3 dager',  days: 3,  price: P[slug]['3d'],  tagline: 'Intensiv repetisjon rett før prøven.' },
  { slug: '14d', label: '14 dager', days: 14, price: P[slug]['14d'], popular: true, tagline: 'Vår anbefaling — god tid på alle temaer.' },
  { slug: '30d', label: '30 dager', days: 30, price: P[slug]['30d'], tagline: 'For deg som starter fra bunnen.' },
];
const PLAN_CATALOG = {
  'klasse-b':   { name: 'Teoriprøven (Klasse B)', icon: 'Car',   blurb: 'Bil · 1 000+ spørsmål og skilt', tiers: STD_TIERS('klasse-b') },
  'jeger':      { name: 'Jegerprøven',            icon: 'Target',blurb: 'Hele pensum og prøver på tid',   tiers: STD_TIERS('jeger') },
  'drone':      { name: 'Droneprøven A2',         icon: 'Drone', blurb: 'Norsk regelverk og luftrom',     tiers: STD_TIERS('drone') },
  'etablerer':  { name: 'Etablererprøven',        icon: 'Brief', blurb: 'Alt du må kunne til prøven',     tiers: STD_TIERS('etablerer') },
  'skjenke':    { name: 'Skjenkebevilling',       icon: 'Wine',  blurb: 'Kunnskapsprøven – skjenking',    tiers: STD_TIERS('skjenke') },
  'salg':       { name: 'Salgsbevilling',         icon: 'Store', blurb: 'Kunnskapsprøven – salg',         tiers: STD_TIERS('salg') },
  'statsborger':{ name: 'Statsborgerprøven',      icon: 'Flag',  blurb: 'Norsk samfunnskunnskap',         tiers: [
    { slug: 'grunnpakke', label: '10 dager', days: 10, price: P.statsborger.grunnpakke, tagline: 'Rask oppfriskning før prøven.' },
    { slug: 'pluss',      label: '30 dager', days: 30, price: P.statsborger.pluss, popular: true, tagline: 'Vår anbefaling — trygg forberedelse.' },
    { slug: 'premium',    label: '90 dager', days: 90, price: P.statsborger.premium, tagline: 'God tid, helt fra start.' },
  ] },
  // Samfunnskunnskapsprøven — egen nisje siden 14/8 2026 (LP: /samfunnskunnskapsproven/).
  // Premium (90 d) gir også statsborger-tilgang via webhook-grant, derfor taglinen.
  'samfunnskunnskap':{ name: 'Samfunnskunnskapsprøven', icon: 'Book', blurb: 'Permanent opphold og statsborgerskap', tiers: [
    { slug: 'grunnpakke', label: '10 dager', days: 10, price: P.samfunnskunnskap.grunnpakke, tagline: 'Prøven er om få dager.' },
    { slug: 'pluss',      label: '30 dager', days: 30, price: P.samfunnskunnskap.pluss, popular: true, tagline: 'Vår anbefaling — tid nok til hele pensum.' },
    { slug: 'premium',    label: '90 dager', days: 90, price: P.samfunnskunnskap.premium, tagline: 'Statsborgerprøven inkludert + bestått-garanti.' },
  ] },
};
// Display names for plan slugs the app may send that aren't sold here
// directly (graceful fallback → exam picker).
const EXTRA_NAMES = { 'batforer': 'Båtførerprøven', 'mc': 'Teoriprøven (MC)' };

const DEFAULT_PLAN = 'klasse-b'; // biggest market — sensible organic default

function planName(slug) {
  return (PLAN_CATALOG[slug] && PLAN_CATALOG[slug].name) || EXTRA_NAMES[slug] || 'prøven';
}

// ---- DataLayer (GTM-friendly; consent gated downstream). Never include e. ----
function pushDL(event, payload = {}) {
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({ event, ...payload });
}
const tierItem = (slug, t) => ({
  item_id: `${slug}_${t.slug}`, item_name: `${planName(slug)} · ${t.days} dager`,
  item_category: slug, price: t.price, quantity: 1,
});

// ---- Read + scrub URL contract once ----
function useAppParams() {
  return useMemo(() => {
    const p = new URLSearchParams(window.location.search);
    const ctx = {
      fromApp: p.get('from') === 'app',
      plan: p.get('plan') || null,
      email: p.get('e') ? decodeURIComponent(p.get('e')) : '',
    };
    // Scrub query so the email isn't left in the address bar / history.
    if (window.location.search) {
      window.history.replaceState({}, '', window.location.pathname);
    }
    return ctx;
  }, []);
}

const EMAIL_RE = /^\S+@\S+\.\S+$/;

// Fire-and-forget: store a valid email the moment it's entered (blur), even if
// the visitor never clicks "Betal med kort". Store-only — no email is ever
// sent to these captures. Guarded so a given address posts at most once per
// page load. Never throws / never blocks the UI.
let _capturedEmail = null;
function captureEmailLead(email, planSlug, tierSlug) {
  if (!EMAIL_RE.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: captureAttribution() }),
      keepalive: true,
    }).catch(() => {});
  } catch (_) { /* ignore */ }
}

// ---- Attribution (UTM + ad click-IDs) ----
// Mirrors the niche-LP pattern (Boat/pricing-page.jsx): first-touch UTMs +
// gclid/gbraid/wbraid/fbclid/ttclid in sessionStorage, sent with checkout +
// email capture
// so stripe-checkout can stamp them on the Stripe session (CAC/CAPI truth).
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);
      // 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,
  };
  try { sessionStorage.setItem(STORAGE_KEY, JSON.stringify(attr)); } catch (_) {}
  return attr;
}
// Eager first-touch capture: useAppParams() scrubs the ENTIRE query string on
// first render, so this must run at script-eval time while the URL is intact.
captureAttribution();

// ---- Header ----
function Nav({ onCta }) {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const f = () => setScrolled(window.scrollY > 8);
    window.addEventListener('scroll', f, { passive: true });
    return () => window.removeEventListener('scroll', f);
  }, []);
  return (
    <header className={`nav ${scrolled ? 'scrolled' : ''}`}>
      <div className="wrap nav-inner">
        <a href="/" className="brand">
          <img className="brand-mark" src="favicon.svg?v=20260706b" width="30" height="30" alt="" />
          <span>Teorihuset</span>
        </a>
        <button type="button" className="btn btn-primary btn-sm" onClick={onCta}>
          Få tilgang
        </button>
      </div>
    </header>
  );
}

// ---- Exam picker ----
// Soft per-exam accent — adds life + scannability while the card itself
// stays neutral/trustworthy. `c` = icon/hover colour, `soft` = chip tint.
const EXAM_COLORS = {
  'klasse-b':    { c: '#2563EB', soft: '#EAF1FE' },
  'jeger':       { c: '#15803D', soft: '#E7F4EC' },
  'drone':       { c: '#0E7490', soft: '#E1F1F3' },
  'etablerer':   { c: '#6D28D9', soft: '#F0EAFB' },
  'skjenke':     { c: '#BE123C', soft: '#FBE9ED' },
  'salg':        { c: '#B45309', soft: '#FBF0E2' },
  'statsborger': { c: '#B91C1C', soft: '#FBEAEA' },
  'samfunnskunnskap': { c: '#C2417B', soft: '#FBE8F0' },
};

function ExamPicker({ selected, onSelect }) {
  return (
    <div className="exam-grid" role="radiogroup" aria-label="Velg prøve">
      {Object.entries(PLAN_CATALOG).map(([slug, p]) => {
        const Icon = I[p.icon] || I.Book;
        const sel = slug === selected;
        const col = EXAM_COLORS[slug] || { c: 'var(--accent)', soft: 'var(--surface-2)' };
        return (
          <button
            key={slug} type="button" role="radio" aria-checked={sel}
            className={`exam-card ${sel ? 'is-selected' : ''}`}
            style={{ '--c': col.c, '--soft': col.soft }}
            onClick={() => onSelect(slug)}
          >
            <span className="exam-card-icon"><Icon size={24} /></span>
            <span className="exam-card-text">
              <span className="exam-card-name">{p.name}</span>
              <span className="exam-card-sub">{p.blurb}</span>
            </span>
            <span className="exam-card-go" aria-hidden="true"><I.Arrow size={18} /></span>
            <span className="exam-card-check" aria-hidden="true"><I.Check size={14} /></span>
          </button>
        );
      })}
    </div>
  );
}

// ---- Device mockup (faithful, lightweight render of the app) ----
const SAMPLE_Q = {
  'klasse-b':   { q: 'Du nærmer deg et gangfelt. En fotgjenger venter ved kanten. Hva gjør du?', opts: ['Øker farten forbi', 'Stopper og slipper fotgjengeren over', 'Tuter for å varsle'], a: 1 },
  'jeger':      { q: 'Når er det forsvarlig å løsne skudd mot et dyr?', opts: ['Når sikker bakgrunn og rein felling er mulig', 'Så snart dyret er innen rekkevidde', 'Når andre jegere ser på'], a: 0 },
  'drone':      { q: 'Hva må du alltid ha i kontrollert luftrom (CTR)?', opts: ['Ingenting ekstra', 'Klarering fra lufttrafikktjenesten', 'Kun refleksvest'], a: 1 },
  'batforer':   { q: 'Du møter en seilbåt for seil i et trangt farvann. Hvem viker?', opts: ['Motorbåten viker for seilbåten', 'Seilbåten viker for motorbåten', 'Den minste båten viker først'], a: 0 },
  'statsborger':{ q: 'Hvem velger representantene til Stortinget?', opts: ['Regjeringen', 'Kongen', 'Innbyggerne ved valg'], a: 2 },
  'samfunnskunnskap':{ q: 'Hvor mange representanter sitter på Stortinget?', opts: ['150', '169', '200'], a: 1 },
  'etablerer':  { q: 'Når må virksomheten registreres i Merverdiavgiftsregisteret?', opts: ['Etter første krone i avgiftspliktig omsetning', 'Når avgiftspliktig omsetning passerer 50 000 kr på 12 måneder', 'Først når bedriften får ansatte'], a: 1 },
  'skjenke':    { q: 'Hva er laveste alder for å bli skjenket brennevin (over 22 vol-%)?', opts: ['18 år', '20 år', '21 år'], a: 1 },
  'salg':       { q: 'Inntil hvilket klokkeslett kan dagligvarebutikker selge øl på hverdager?', opts: ['Kl. 18:00', 'Kl. 20:00', 'Kl. 22:00'], a: 1 },
};
const DEFAULT_Q = { q: 'Hva er riktig i denne situasjonen?', opts: ['Alternativ A', 'Riktig alternativ', 'Alternativ C'], a: 1 };

// Display names for the hero mockup carousel — includes batforer which is
// not in PLAN_CATALOG (not sold from this page directly).
const DEVICE_TITLES = {
  'klasse-b':   'Teoriprøven (Klasse B)',
  'jeger':      'Jegerprøven',
  'batforer':   'Båtførerprøven',
  'drone':      'Droneprøven A2',
  'statsborger':'Statsborgerprøven',
  'samfunnskunnskap':'Samfunnskunnskapsprøven',
};
// Per-niche emoji shown after the title inside the device mockup. We
// render Twemoji SVGs directly (no JS lib) so the glyph looks the same
// on Windows/Chrome — native flag emoji in particular renders as "NO"
// letters on Windows. Pinned to the maintained jdecked/twemoji fork.
const TWEMOJI_BASE = 'https://cdn.jsdelivr.net/gh/jdecked/twemoji@v15.1.0/assets/svg/';
const DEVICE_EMOJI = {
  'klasse-b':   '1f697',        // 🚗
  'jeger':      '1f98c',        // 🦌
  'batforer':   '26f5',         // ⛵
  'drone':      '1f681',        // 🚁
  'statsborger':'1f1f3-1f1f4',  // 🇳🇴
  'samfunnskunnskap':'1f3db',   // 🏛️
  'etablerer':  '1f4bc',        // 💼
  'skjenke':    '1f377',        // 🍷
  'salg':       '1f3ea',        // 🏪
};

// Hero mockup rotates through these when no specific plan is targeted —
// signals the breadth of the platform without a heavy "logo soup".
const ROTATION_PLANS = ['klasse-b', 'jeger', 'batforer', 'statsborger'];
const ROTATION_MS = 4200;   // dwell per slide
const SWAP_MS     = 650;    // fade-out duration before content swaps in

function DeviceMock({ plan, rotate }) {
  const [idx, setIdx] = useState(0);
  const [visible, setVisible] = useState(true);

  useEffect(() => {
    if (!rotate) return;
    let swapTimer;
    const tick = setInterval(() => {
      setVisible(false);
      swapTimer = setTimeout(() => {
        setIdx(i => (i + 1) % ROTATION_PLANS.length);
        setVisible(true);
      }, SWAP_MS);
    }, ROTATION_MS);
    return () => { clearInterval(tick); clearTimeout(swapTimer); };
  }, [rotate]);

  const activePlan = rotate ? ROTATION_PLANS[idx] : plan;
  const meta = SAMPLE_Q[activePlan] ? activePlan : DEFAULT_PLAN;
  const item = SAMPLE_Q[meta] || DEFAULT_Q;
  const title = DEVICE_TITLES[meta] || (PLAN_CATALOG[meta] && PLAN_CATALOG[meta].name) || 'Teoriprøven';
  const swapCls = `appx-swap ${visible ? 'is-in' : 'is-out'}`;
  return (
    <div className="device" role="img" aria-label={`Skjermbilde fra Teorihuset-appen — ${title}`}>
      <div className="device-screen">
        <div className="appx-top" />
        <div className="appx-bar">
          <span className="appx-back"><I.ChevronLeft size={15} /> Tilbake</span>
          <span className="appx-count">14 / 45</span>
          <span className="appx-menu"><I.Menu size={17} /></span>
        </div>
        <div className="appx-body">
          <div className={`appx-plan ${swapCls}`}>
            {title}
            {DEVICE_EMOJI[meta] && (
              <img
                className="appx-title-emoji"
                src={`${TWEMOJI_BASE}${DEVICE_EMOJI[meta]}.svg`}
                alt="" aria-hidden="true" draggable="false"
              />
            )}
          </div>
          <div className={swapCls}>
            <div className="appx-q">{item.q}</div>
            <div className="appx-opts">
              {item.opts.map((o, i) => (
                <div key={i} className={`appx-opt ${i === item.a ? 'is-correct' : ''}`}>
                  <span className="appx-opt-text">{o}</span>
                  {i === item.a && (
                    <span className="appx-opt-check" aria-hidden="true"><I.Check size={12} /></span>
                  )}
                </div>
              ))}
            </div>
          </div>
          <div className="appx-btn">Neste</div>
        </div>
      </div>
    </div>
  );
}

// ---- Hero ----
function Hero({ ctx, personalised, planForCopy, devicePlan, primaryLabel, onPrimary, rotateDevice }) {
  return (
    <section className="hero">
      <svg className="hero-bg-svg" viewBox="0 0 1200 720" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
        <defs>
          <linearGradient id="hp-fade" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0" stopColor="currentColor" stopOpacity="0" />
            <stop offset="0.18" stopColor="currentColor" stopOpacity="1" />
            <stop offset="0.82" stopColor="currentColor" stopOpacity="1" />
            <stop offset="1" stopColor="currentColor" stopOpacity="0" />
          </linearGradient>
        </defs>
        <g fill="none" stroke="url(#hp-fade)" strokeWidth="1.25" strokeLinecap="round">
          <path d="M -50 130 C 220 70, 520 200, 820 110 S 1100 60, 1300 130" />
          <path d="M -50 250 C 250 160, 550 320, 850 220 S 1100 170, 1300 250" />
          <path d="M -50 370 C 280 260, 580 440, 880 320 S 1100 270, 1300 370" />
          <path d="M -50 500 C 310 380, 610 580, 910 450 S 1100 390, 1300 500" />
          <path d="M -50 630 C 340 500, 640 700, 940 580 S 1100 510, 1300 630" />
        </g>
      </svg>
      <div className="wrap hero-inner">
        <div className="hero-copy">
          {ctx.fromApp && (
            <span className="hero-continuity fade-up">
              <I.Lock size={14} /> Fullfør tilgangen til appen her
            </span>
          )}
          <h1 className="fade-up" style={{ animationDelay: '.04s' }}>
            {personalised
              ? `Få full tilgang til ${planForCopy}`
              : 'Bestå teoriprøven — øv smart, ikke hardt.'}
          </h1>
          <p className="lead fade-up" style={{ animationDelay: '.09s' }}>
            {personalised
              ? 'Du er allerede logget inn i appen. Lås opp hele øvingsbanken, prøver på tid og fremgang synkronisert mellom enhetene dine.'
              : 'Oppdaterte spørsmålsbanker, realistiske prøver på tid og forklaringer — for førerkort, jeger, drone, statsborgerskap og flere prøver.'}
          </p>
          <div className="hero-cta-row fade-up" style={{ animationDelay: '.14s' }}>
            <button type="button" className="btn btn-primary btn-lg" style={{ width: 'auto' }} onClick={onPrimary}>
              {primaryLabel} <I.Arrow size={16} />
            </button>
          </div>
          <div className="seal fade-up" style={{ animationDelay: '.18s' }}>
            <span className="seal-mark"><I.Shield size={18} /></span>
            <span className="seal-text">
              <b>Bestått-garanti</b>
              <span>Stryker du på prøven? Full refusjon.</span>
            </span>
          </div>
          <div className="hero-microtrust fade-up" style={{ animationDelay: '.22s' }}>
            <span><I.Check size={15} /> Engangsbeløp — ikke abonnement</span>
            <span><I.Lock size={15} /> Sikker betaling via Stripe</span>
          </div>
        </div>
        <div className="hero-visual fade-up" style={{ animationDelay: '.12s' }}>
          <DeviceMock plan={devicePlan} rotate={rotateDevice} />
        </div>
      </div>
    </section>
  );
}

// ---- Picker section (only when not personalised) ----
function PickerSection({ selected, onSelect }) {
  return (
    <section id="picker" style={{ background: 'var(--surface)', borderBottom: '1px solid var(--line)' }}>
      <div className="wrap">
        <div className="section-head">
          <h2>Hva øver du til?</h2>
          <p>Velg prøven din — så får du riktig øvingsbank og pakke.</p>
        </div>
        <ExamPicker selected={selected} onSelect={onSelect} />
      </div>
    </section>
  );
}

// ---- What you unlock ----
function Features() {
  const items = [
    { ic: 'Book',  t: 'Hele øvingsbanken',     d: 'Alle spørsmål for prøven din — ikke et utvalg.' },
    { ic: 'Timer', t: 'Prøver på tid',         d: 'Realistisk simulering med riktig tid og beståttgrense.' },
    { ic: 'Sync',  t: 'Synkronisert fremgang', d: 'Øvingen din følger med mellom mobil og nettleser.' },
    { ic: 'Bulb',  t: 'Forklaringer',          d: 'Forstå hvorfor svaret er riktig — ikke bare pugg.' },
    { ic: 'Book',  t: 'Teorikurs',             d: 'Les deg opp på pensum i samme app.' },
  ];
  return (
    <section style={{ background: 'var(--surface)', borderTop: '1px solid var(--line)', borderBottom: '1px solid var(--line)' }}>
      <div className="wrap">
        <div className="section-head">
          <h2>Dette låser du opp i appen du allerede har</h2>
          <p>Kjøpet åpner full tilgang på kontoen din — med en gang.</p>
        </div>
        <div className="features">
          {items.map((f, i) => {
            const Ic = I[f.ic];
            return (
              <div className="feature" key={i}>
                <span className="feature-ic"><Ic size={18} /></span>
                <div><h3>{f.t}</h3><p>{f.d}</p></div>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

// ---- Pricing + checkout ----
function PricingCheckout({ slug, ctx, email, setEmail, emailLocked, sentinelRef }) {
  const plan = PLAN_CATALOG[slug] || PLAN_CATALOG[DEFAULT_PLAN];
  const tiers = plan.tiers;
  const [tierSlug, setTierSlug] = useState((tiers.find(t => t.popular) || tiers[0]).slug);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const tier = tiers.find(t => t.slug === tierSlug) || tiers[0];

  useEffect(() => {
    setTierSlug((tiers.find(t => t.popular) || tiers[0]).slug);
  }, [slug]);

  useEffect(() => {
    pushDL('view_item_list', { ecommerce: { item_list_name: `${slug}_pricing`, items: tiers.map(t => tierItem(slug, t)) } });
  }, [slug]);

  const selectTier = (ts) => {
    setTierSlug(ts); setError(null);
    const t = tiers.find(x => x.slug === ts);
    pushDL('select_item', { ecommerce: { items: [tierItem(slug, t)] } });
  };

  const emailValid = EMAIL_RE.test(email);

  const pay = async () => {
    setError(null);
    if (!emailValid) { setError('Skriv inn en gyldig e-postadresse.'); return; }
    pushDL('begin_checkout', { ecommerce: { currency: CURRENCY, value: tier.price, items: [tierItem(slug, tier)] } });
    setLoading(true);
    try {
      const res = await fetch(STRIPE_CHECKOUT_FN, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          planSlug: slug,
          tierSlug: tier.slug,
          customer_email: email,
          attribution: captureAttribution(),
          // plan+tier lets Takk price the purchase-event (GA4/Ads) client-side.
          success_url: `${BASE_URL}/Takk?session_id={CHECKOUT_SESSION_ID}&plan=${slug}&tier=${tier.slug}`,
          cancel_url: `${BASE_URL}/?plan=${slug}`,
        }),
      });
      const data = await res.json();
      if (!res.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 (
    <section id="checkout">
      <div className="wrap">
        <div className="section-head">
          <h2>Velg tilgang til {plan.name}</h2>
          <p>Engangsbeløp, ingen abonnement. Velg perioden som passer prøvedatoen din.</p>
        </div>

        <div className="checkout-grid">
          <div>
            <div className="tier-list" role="radiogroup" aria-label="Velg pakke">
              {tiers.map(t => {
                const sel = t.slug === tierSlug;
                return (
                  <button
                    key={t.slug} type="button" role="radio" aria-checked={sel}
                    className={`tier ${t.popular ? 'tier--popular' : ''} ${sel ? 'is-selected' : ''}`}
                    onClick={() => selectTier(t.slug)}
                  >
                    {t.popular && <span className="tier-ribbon">Mest populær</span>}
                    <span className="tier-radio" aria-hidden="true">{sel && <I.Check size={12} />}</span>
                    <span className="tier-body">
                      <span className="tier-name">{t.label}</span>
                      <span className="tier-tagline">{t.tagline}</span>
                    </span>
                    <span className="tier-price">
                      <span className="tier-amount">{t.price} kr</span>
                      <span className="tier-vat" style={{ display: 'block' }}>inkl. mva.</span>
                    </span>
                  </button>
                );
              })}
            </div>
            <div ref={sentinelRef} aria-hidden="true" style={{ height: 1 }} />
          </div>

          <div className="checkout">
            <div className="checkout-summary">
              <div className="checkout-sumrow">
                <strong>{plan.name} · {tier.days} dager</strong>
                <span className="checkout-sumamt">{tier.price} kr</span>
              </div>
              <div className="checkout-subnote">Engangsbeløp. Tilgang umiddelbart etter betaling.</div>
            </div>

            <label className="field">
              <span className="field-label">E-post</span>
              <input
                type="email" inputMode="email" autoComplete="email"
                className="field-input" placeholder="navn@eksempel.no"
                value={email} readOnly={emailLocked}
                onChange={e => !emailLocked && setEmail(e.target.value)}
                onBlur={() => !emailLocked && captureEmailLead(email, slug, tier.slug)}
              />
              {emailLocked ? (
                <span className="field-hint is-locked">
                  <I.Lock size={13} /> Tilgang knyttes til kontoen din i appen. Bruk denne e-posten.
                </span>
              ) : ctx.fromApp ? (
                <span className="field-hint is-warn">
                  <I.Shield size={13} /> Bruk samme e-post som du logget inn med i appen — ellers låses ikke tilgangen opp der.
                </span>
              ) : (
                <span className="field-hint">
                  <I.Mail size={13} /> Vi sender innloggingslenke og kvittering hit.
                </span>
              )}
            </label>

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

            <button
              type="button" className="btn btn-primary btn-lg"
              onClick={pay} disabled={!emailValid || loading}
            >
              {loading ? 'Sender til betaling…' : <>Betal med kort <I.Arrow size={16} /></>}
            </button>

            <div className="checkout-trust">
              <span><I.Lock size={13} /> Sikker betaling via Stripe</span>
              <span><I.Shield size={13} /> Bestått-garanti</span>
            </div>
            <div className="checkout-legal">
              Ved å gå videre godtar du <a href="Vilkar">Vilkår</a> og <a href="Personvern">Personvern</a>.
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

// ---- Trust ----
function Trust() {
  const quotes = [
    { p: '«Helt likt den ekte prøven. Besto på første forsøk.»', by: 'Maria, Klasse B' },
    { p: '«Forklaringene gjorde at jeg faktisk forsto reglene.»', by: 'Jonas, Jegerprøven' },
    { p: '«Kunne øve på bussen og fortsette hjemme. Smidig.»', by: 'Ahmed, Statsborgerprøven' },
  ];
  return (
    <section style={{ background: 'var(--surface)', borderTop: '1px solid var(--line)' }}>
      <div className="wrap">
        <div className="section-head">
          <h2>Trygt valg før prøven</h2>
          <p>Tusenvis av kandidater øver hos oss hvert år.</p>
        </div>
        <div className="trust-band">
          <span className="trust-chip"><I.Shield size={14} /> Bestått-garanti</span>
          <span className="trust-chip"><I.Check size={14} /> Engangsbeløp — ikke abonnement</span>
          <span className="trust-chip"><I.Phone size={14} /> Norsk support</span>
          <span className="trust-chip"><I.Lock size={14} /> Sikker betaling via Stripe</span>
        </div>
        <div className="quotes">
          {quotes.map((q, i) => (
            <div className="quote" key={i}><p>{q.p}</p><div className="quote-by">{q.by}</div></div>
          ))}
        </div>
        <div className="company-line">
          <span>Norsk kundeservice — hei@teorihuset.no</span>
        </div>
      </div>
    </section>
  );
}

// ---- FAQ ----
function FAQ() {
  const items = [
    { q: 'Jeg lastet ned appen — hvorfor betaler jeg her?', a: 'Teorihuset er en nettbasert læringsplattform, og appen er måten du øver på. Selve tilgangen kjøper og administrerer du her på teorihuset.no. Etter kjøp er alt låst opp i appen du allerede har.' },
    { q: 'Hvordan får jeg tilgang i appen etter kjøp?', a: 'Bruk samme e-post i kjøpet som du er logget inn med i appen. Tilgangen knyttes til den e-posten, og appen låses opp automatisk neste gang du åpner den. Du får også en innloggingslenke og kvittering på e-post.' },
    { q: 'Hva dekker bestått-garantien?', a: 'Gjennomfører du det anbefalte opplegget i appen og likevel stryker, refunderer vi hele beløpet mot fremvist prøveresultat. Kontakt support, så ordner vi resten.' },
    { q: 'Er det abonnement?', a: 'Nei. Du betaler én gang for den perioden du velger. Ingen automatisk fornyelse, ingen binding.' },
    { q: 'Hvor lenge varer tilgangen?', a: 'Tilgangen varer for perioden du velger (f.eks. 14 dager) fra kjøpstidspunktet. Trenger du mer tid kan du kjøpe en ny periode.' },
  ];
  const [open, setOpen] = useState(0);
  return (
    <section>
      <div className="wrap narrow">
        <div className="section-head"><h2>Vanlige spørsmål</h2></div>
        <div className="faq-list">
          {items.map((it, i) => (
            <div className={`faq-item ${open === i ? 'open' : ''}`} key={i}>
              <button type="button" className="faq-q" aria-expanded={open === i} onClick={() => setOpen(open === i ? -1 : i)}>
                <span>{it.q}</span>
                <span className="faq-icon"><I.Plus size={16} /></span>
              </button>
              <div className="faq-a"><div className="faq-a-inner"><p>{it.a}</p></div></div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ---- Footer ----
function Footer() {
  return (
    <footer>
      <div className="wrap">
        <div className="footer-grid">
          <div className="footer-brand">
            <a href="/" className="brand">
              <img className="brand-mark" src="favicon.svg?v=20260706b" width="30" height="30" alt="" />
              <span>Teorihuset</span>
            </a>
            <p>Norsk øvingsplattform for teoretiske prøver — førerkort, jeger, drone, statsborgerskap og flere.</p>
          </div>
          <div className="footer-col">
            <h4>Produkt</h4>
            <ul>
              <li><a href="#checkout">Priser</a></li>
              <li><a href="#faq">Spørsmål</a></li>
            </ul>
          </div>
          <div className="footer-col">
            <h4>Selskap</h4>
            <ul>
              <li><a href="Personvern">Personvern</a></li>
              <li><a href="Vilkar">Vilkår</a></li>
              <li><a href="mailto:hei@teorihuset.no">Kontakt</a></li>
              <li><button type="button" className="footer-col-btn" onClick={() => window.openConsentPreferences && window.openConsentPreferences()}>Endre samtykke</button></li>
            </ul>
          </div>
        </div>
        {/* CROSS_LP_START */}
        <div className="ft-cross-lp" aria-labelledby="ft-cross-lp-label">
          <span id="ft-cross-lp-label" className="ft-cross-lp-label">Andre prøver:</span>
          <nav className="ft-cross-lp-nav">
            <a href="/teoriproven/" className="ft-cross-lp-link">Teoriprøve Klasse B</a>
            <a href="/batforerproven/" className="ft-cross-lp-link">Båtførerprøven</a>
            <a href="/dronesertifikat/" className="ft-cross-lp-link">Dronesertifikat A2</a>
            <a href="/jegerproven/" className="ft-cross-lp-link">Jegerprøven</a>
            <a href="/statsborgerproven/" className="ft-cross-lp-link">Statsborgerprøven</a>
            <a href="/samfunnskunnskapsproven/" className="ft-cross-lp-link">Samfunnskunnskapsprøven</a>
            <a href="/bevilling/etablerer" className="ft-cross-lp-link">Etablererprøven</a>
            <a href="/bevilling/skjenke" className="ft-cross-lp-link">Skjenkebevilling</a>
            <a href="/bevilling/salg" className="ft-cross-lp-link">Salgsbevilling</a>
            <a href="/bevilling/kontrollor" className="ft-cross-lp-link">Kontrollørprøven</a>
          </nav>
        </div>
        {/* CROSS_LP_END */}

        <div className="footer-bottom">
          <span>© 2026 teorihuset.no — Made in Norway</span>
          <span>Made in Norway</span>
        </div>
      </div>
    </footer>
  );
}

// ---- Mobile sticky bar ----
function StickyBar({ plan, tier, visible, onCta }) {
  if (!plan || !tier) return null;
  return (
    <div className={`stickybar ${visible ? 'is-visible' : ''}`} aria-hidden={!visible}>
      <div className="stickybar-inner">
        <div className="stickybar-meta">
          <span className="stickybar-label">{planName(plan)} · {tier.days} dager</span>
          <span className="stickybar-amt">{tier.price} kr</span>
        </div>
        <button type="button" className="btn btn-primary btn-sm" onClick={onCta}>Velg pakke →</button>
      </div>
    </div>
  );
}

// ---- App root ----
function HomeApp() {
  const ctx = useAppParams();

  // Personalise when we know the exact exam (app sent a plan we sell, or a
  // ?plan= deep link). Otherwise show the picker.
  const knownPlan = !!(ctx.plan && PLAN_CATALOG[ctx.plan]);
  const initialPlan = knownPlan ? ctx.plan : DEFAULT_PLAN;

  // Niche-tint: when arriving from a known plan, paint a few signature
  // surfaces in a shared "app continuation" teal (hero glow, continuity
  // chip, popular tier, feature icons). Cold visitors keep the default
  // brand-blue. Remove this block + the niche CSS at the bottom of
  // styles.css to fully revert.
  useEffect(() => {
    if (!knownPlan) return;
    document.body.style.setProperty('--accent-niche', '#0E7490');
    document.body.style.setProperty('--niche-soft', '#E1F1F3');
    document.body.classList.add('niche-tinted');
    return () => {
      document.body.style.removeProperty('--accent-niche');
      document.body.style.removeProperty('--niche-soft');
      document.body.classList.remove('niche-tinted');
    };
  }, [knownPlan]);

  const [slug, setSlug] = useState(initialPlan);
  const [email, setEmail] = useState(ctx.email || '');
  // Lock the email when the app supplied a valid one (continuity guarantee).
  const emailLocked = ctx.fromApp && EMAIL_RE.test(ctx.email || '');

  const [stickyVisible, setStickyVisible] = useState(false);
  const sentinelRef = useRef(null);

  const plan = PLAN_CATALOG[slug] || PLAN_CATALOG[DEFAULT_PLAN];
  const popularTier = plan.tiers.find(t => t.popular) || plan.tiers[0];

  const scrollTo = (id) => {
    const el = document.getElementById(id);
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
  };
  const onPrimary = () => scrollTo(knownPlan ? 'checkout' : 'picker');
  const pickPlan = (s) => { setSlug(s); setTimeout(() => scrollTo('checkout'), 60); };

  useEffect(() => {
    if (!sentinelRef.current) return;
    const obs = new IntersectionObserver(
      ([e]) => setStickyVisible(!e.isIntersecting),
      { rootMargin: '0px 0px -45% 0px' }
    );
    obs.observe(sentinelRef.current);
    return () => obs.disconnect();
  }, []);

  return (
    <>
      <Nav onCta={onPrimary} />
      <main>
        <Hero
          ctx={ctx}
          personalised={knownPlan}
          planForCopy={planName(ctx.plan)}
          devicePlan={knownPlan ? ctx.plan : DEFAULT_PLAN}
          rotateDevice={!knownPlan}
          primaryLabel={knownPlan ? 'Velg pakke' : 'Kom i gang'}
          onPrimary={onPrimary}
        />
        {!knownPlan && <PickerSection selected={slug} onSelect={pickPlan} />}
        <Features />
        <PricingCheckout
          slug={slug} ctx={ctx}
          email={email} setEmail={setEmail} emailLocked={emailLocked}
          sentinelRef={sentinelRef}
        />
        <Trust />
        <div id="faq"><FAQ /></div>
      </main>
      <StickyBar plan={slug} tier={popularTier} visible={stickyVisible} onCta={onPrimary} />
      <Footer />
    </>
  );
}

window.HomeApp = HomeApp;
