// Agency Audit, lay-out "keuring" (ontwerp Kevin, oktober 2026): een lichte pagina
// met keuringsbon, keuringsstraat, microvragen tijdens het wachten, een compacte
// bon als rapport en een second opinion in drie vormen. Draait als testvariant
// (config.layout === 'keuring') naast de basis; meting, API's en regels zijn gelijk.
// Animaties via GSAP als die geladen is en de bezoeker geen "minder beweging" heeft.

const KEURING_PUNTEN = [
  { id: 'ads', nr: '01', title: 'Advertenties', text: 'Draaien er advertenties op Google en Meta? En worden ze ook vernieuwd, of staan ze al maanden stil?', dim: 'activity', sources: ['transparency-center', 'meta-ad-library'] },
  { id: 'tracking', nr: '02', title: 'Meting', text: 'Staat de meting goed, zodat iemand kan zien wat je advertenties opleveren?', dim: 'tracking', sources: ['tag-detect'] },
  { id: 'seo', nr: '03', title: 'Vindbaarheid', text: 'Hoe sta je in Google ten opzichte van vergelijkbare bedrijven?', dim: 'visibility', sources: ['seo-data', 'seo-competitors', 'serp-keywords'] },
  { id: 'competitors', nr: '04', title: 'Concurrenten', text: 'Wie adverteert er op jouw zoektermen, en op je eigen merknaam?', dim: 'brand_bidding', sources: ['serp-brand'] },
  { id: 'landing', nr: '05', title: "Landingspagina's", text: "Sluiten de pagina's achter je advertenties aan op wat je belooft?", dim: 'landing', sources: ['landing-pages', 'page-speed'] },
];
// Desktop-hero: rouleren van voorbeeldbonnen (mobiel toont alleen de eerste, stil)
const KEURING_VOORBEELDEN = [
  { domain: 'voorbeeldbedrijf.nl', rows: [['Advertenties worden bijgestuurd', 'ok', 'Goed'], ['Meting van campagnes', 'fail', 'Afgekeurd'], ['Zichtbaar op eigen merknaam', 'warn', 'Let op']], stamp: 'Herkeuring', ok: false },
  { domain: 'installatiebedrijf.nl', rows: [['Nieuwe advertenties op Google', 'fail', '7 maanden stil'], ['Meting van campagnes', 'ok', 'Goed'], ['Landingspagina na de advertentie', 'warn', 'Traag']], stamp: 'Herkeuring', ok: false },
  { domain: 'webshopvoorbeeld.nl', rows: [['Advertenties worden bijgestuurd', 'ok', 'Goed'], ['Groei tegenover vergelijkbare shops', 'ok', 'Goed'], ['Concurrent op je merknaam', 'ok', 'Niemand']], stamp: 'Goedgekeurd', ok: true },
];
const KEURING_CHIPS = [['Google Ads', '40 advertenties gevonden'], ['Meta', '30 actief, nieuwste gisteren'], ['GA4 en conversiemeting', 'geconfigureerd'], ['Merknaam', '2 concurrenten bieden mee']];
const kDesk = () => window.matchMedia('(min-width: 960px)').matches;
const KEURING_LIGHT = { green: ['ok', 'Goedgekeurd'], orange: ['warn', 'Let op'], red: ['fail', 'Afgekeurd'], grey: ['none', 'Niet gekeurd'] };
const KEURING_STAMP = { stay: 'Goedgekeurd', talk: 'Let op', consider_switch: 'Herkeuring<br>aanbevolen', insufficient_data: 'Niet volledig<br>gekeurd' };

const kMotion = () => (typeof window.gsap !== 'undefined' && !window.matchMedia('(prefers-reduced-motion: reduce)').matches ? window.gsap : null);
function kStampIn(el, delay) {
  const g = kMotion(); if (!el || !g) return;
  const rot = parseFloat(getComputedStyle(el).getPropertyValue('--rot')) || (el.closest('.bm-report') ? -10 : -8);
  g.fromTo(el, { scale: 1.6, rotation: rot, autoAlpha: 0 }, { scale: 1, rotation: rot, autoAlpha: 1, duration: 0.45, delay: delay || 0, ease: 'back.out(2.2)', onComplete: () => { if (el.parentElement) g.fromTo(el.parentElement, { x: -2 }, { x: 0, duration: 0.25, ease: 'elastic.out(1, .3)' }); } });
}
function kCountUp(el, end, delay) {
  const g = kMotion(); if (!el || !g) { if (el) el.textContent = String(end); return; }
  const o = { v: 0 }; g.to(o, { v: end, duration: 1, delay: delay || 0, ease: 'power2.out', onUpdate: () => { el.textContent = String(Math.round(o.v)); } });
}
function kMagnet(el) {
  const g = kMotion(); if (!el || !g || window.matchMedia('(hover: none)').matches) return () => {};
  const xTo = g.quickTo(el, 'x', { duration: 0.4, ease: 'power3' }); const yTo = g.quickTo(el, 'y', { duration: 0.4, ease: 'power3' });
  const move = (e) => { const r = el.getBoundingClientRect(); xTo((e.clientX - r.left - r.width / 2) * 0.18); yTo((e.clientY - r.top - r.height / 2) * 0.3); };
  const leave = () => { xTo(0); yTo(0); };
  el.addEventListener('mousemove', move); el.addEventListener('mouseleave', leave);
  return () => { el.removeEventListener('mousemove', move); el.removeEventListener('mouseleave', leave); };
}
const kNorm = (v) => String(v || '').trim().replace(/^https?:\/\//i, '').replace(/^www\./i, '').replace(/\/.*$/, '').toLowerCase();
const kSignal = (ev, ref, value) => { try { window.lpSignals?.push(ev, ref ?? null, value ?? null); } catch { /* bijzaak */ } };

// Keuringsbon (voorbeeld in de hero en het echte rapport delen deze rijen)
function KeuringBonRows({ rows }) {
  return rows.map((r, i) => <div key={i} className="bm-bon__row"><span>{r.label}</span><strong className={`bm-status--${r.tone}`}>{r.status}</strong></div>);
}

// ── Landing ──────────────────────────────────────────────────────────────
function KeuringLanding({ onStart, serverError, busy, cfg }) {
  const { useState, useRef, useEffect } = React;
  const rootRef = useRef(null); const formRef = useRef(null); const straatRef = useRef(null); const trackRef = useRef(null); const barRef = useRef(null); const btnRef = useRef(null); const h1Ref = useRef(null);
  const [url, setUrl] = useState(''); const [error, setError] = useState(''); const [active, setActive] = useState(0); const [sticky, setSticky] = useState(false);
  const [ex, setEx] = useState(0); const exRef = useRef(0); const stageRef = useRef(null);
  const [recognized, setRecognized] = useState(null);
  const reviews = useReviewsData();
  useLandingSignals(rootRef);
  useLeadinfoSuggest(setRecognized);

  useEffect(() => {
    const g = kMotion(); const cleanups = [];
    if (g) {
      const root = rootRef.current;
      const tl = g.timeline({ defaults: { ease: 'power3.out' } });
      tl.from(root.querySelectorAll('[data-anim="fade"]'), { y: 24, autoAlpha: 0, duration: 0.6, stagger: 0.08 }, 0);
      const h1 = h1Ref.current;
      if (h1 && !h1.dataset.split) {
        const words = h1.textContent.trim().split(/\s+/); h1.setAttribute('aria-label', h1.textContent.trim()); h1.dataset.split = '1';
        h1.innerHTML = words.map((w) => `<span aria-hidden="true" style="display:inline-block;overflow:hidden;vertical-align:top"><span class="bm-w" style="display:inline-block">${w}</span></span>`).join(' ');
        tl.from(h1.querySelectorAll('.bm-w'), { yPercent: 110, duration: 0.7, stagger: 0.05 }, 0.1);
      }
      const bon = root.querySelector('[data-anim="bon"]'); if (bon) tl.from(bon, { y: 40, rotation: 4, autoAlpha: 0, duration: 0.8 }, 0.35);
      tl.add(() => { kStampIn(root.querySelector('[data-anim="stamp"]')); kCountUp(root.querySelector('[data-countup]'), 12); }, 1);
      // markeerstreep onder het kernwoord in de kop (keuren, keuring, audit)
      const mark = Array.from(h1?.querySelectorAll('.bm-w') || []).find((w) => /keur|audit/i.test(w.textContent));
      if (mark && kDesk()) { mark.classList.add('bm-mark'); tl.to(mark, { backgroundSize: '100% 100%', duration: 0.5, ease: 'power2.out' }, 1.05); }
      if (kDesk()) {
        // Desktop-hero: zwevende meetchips, scanstraal over de bon, 3D-kanteling met de muis, rouleren van bonnen
        const chips = root.querySelectorAll('.bm-chip');
        tl.from(chips, { y: 30, autoAlpha: 0, scale: 0.9, duration: 0.6, stagger: 0.12 }, 0.9);
        chips.forEach((c, i) => { const t = g.to(c, { y: i % 2 ? '+=10' : '-=10', duration: 2.2 + i * 0.3, yoyo: true, repeat: -1, ease: 'sine.inOut', delay: 1.6 + i * 0.4 }); cleanups.push(() => t.kill()); });
        const beam = root.querySelector('.bm-scan-beam');
        if (beam) { const t = g.fromTo(beam, { top: '0%', autoAlpha: 0 }, { top: '96%', autoAlpha: 1, duration: 1.8, repeat: -1, repeatDelay: 4.2, delay: 2, ease: 'power1.inOut', onRepeat: () => g.set(beam, { autoAlpha: 0 }) }); cleanups.push(() => t.kill()); }
        const stage = stageRef.current; const hero = root.querySelector('.bm-hero');
        if (stage && hero) {
          const rx = g.quickTo(stage, 'rotationX', { duration: 0.6, ease: 'power3' }); const ry = g.quickTo(stage, 'rotationY', { duration: 0.6, ease: 'power3' });
          const move = (e) => { const r = hero.getBoundingClientRect(); const px = (e.clientX - r.left) / r.width - 0.5; const py = (e.clientY - r.top) / r.height - 0.5; ry(px * 6); rx(-py * 4); chips.forEach((c, i) => g.to(c, { x: px * (14 + i * 6), duration: 0.8, ease: 'power3' })); };
          const leave = () => { rx(0); ry(0); chips.forEach((c) => g.to(c, { x: 0, duration: 0.8 })); };
          hero.addEventListener('mousemove', move); hero.addEventListener('mouseleave', leave);
          cleanups.push(() => { hero.removeEventListener('mousemove', move); hero.removeEventListener('mouseleave', leave); });
        }
        const loop = setInterval(() => { const next = (exRef.current + 1) % KEURING_VOORBEELDEN.length; exRef.current = next; setEx(next); }, 9000);
        cleanups.push(() => clearInterval(loop));
      }
      const straat = straatRef.current; const track = trackRef.current;
      if (straat && track && window.ScrollTrigger) {
        g.registerPlugin(window.ScrollTrigger);
        const distance = () => Math.max(0, track.scrollWidth - (track.parentElement?.clientWidth || 0));
        const tween = g.to(track, { x: () => -distance(), ease: 'none', scrollTrigger: { trigger: straat, start: 'center center', end: () => '+=' + (distance() + window.innerHeight * 0.4), pin: true, scrub: 0.6, invalidateOnRefresh: true, onUpdate: (self) => { setActive(Math.min(KEURING_PUNTEN.length - 1, Math.floor(self.progress * KEURING_PUNTEN.length))); if (barRef.current) barRef.current.style.width = 20 + self.progress * 80 + '%'; } } });
        cleanups.push(() => { tween.scrollTrigger?.kill(); tween.kill(); });
      }
      const hoekie = root.querySelector('[data-hoekie]'); if (hoekie) { const t = g.to(hoekie, { y: -6, duration: 1.6, yoyo: true, repeat: -1, ease: 'sine.inOut' }); cleanups.push(() => t.kill()); }
      cleanups.push(kMagnet(btnRef.current));
    }
    let io = null;
    if (formRef.current && 'IntersectionObserver' in window) { io = new IntersectionObserver(([e]) => setSticky(!e.isIntersecting)); io.observe(formRef.current); }
    return () => { cleanups.forEach((c) => c && c()); io?.disconnect(); };
  }, []);

  useEffect(() => {
    const g = kMotion(); const root = rootRef.current; if (!g || !root || ex === 0 && !exRef.current) return;
    const bon = root.querySelector('.bm-hero__bon .bm-bon'); if (!bon) return;
    const tl = g.timeline();
    // ruime inset zodat de stempel (die onder de bon uitsteekt) niet wordt afgesneden; daarna de clip weghalen
    tl.fromTo(bon, { clipPath: 'inset(-40px -40px 100% -40px)' }, { clipPath: 'inset(-40px -40px -70px -40px)', duration: 0.9, ease: 'power2.inOut' })
      .from(bon.querySelectorAll('.bm-bon__row'), { x: -10, autoAlpha: 0, duration: 0.35, stagger: 0.18 }, 0.3)
      .from(bon.querySelectorAll('.bm-bon__row strong'), { scale: 1.5, autoAlpha: 0, duration: 0.3, stagger: 0.18, ease: 'back.out(2)' }, 0.5)
      .add(() => kStampIn(bon.querySelector('.bm-stamp')), 1.2)
      .set(bon, { clearProps: 'clipPath' }, 1.3);
    return () => tl.kill();
  }, [ex]);

  function submit(e) {
    e.preventDefault();
    const d = kNorm(url);
    kSignal('cta_click');
    if (!d || !d.includes('.')) { kSignal('form_error', d ? 'geen-punt' : 'leeg'); setError('Vul een website in, bijvoorbeeld jouwbedrijf.nl'); return; }
    setError(''); try { window.lpSignals?.flush(false); } catch { /* bijzaak */ }
    onStart({ website: d, email: '', concerns: [], companyName: '', industry: '', agencyName: '', keywords: [] }, null);
  }
  const interacted = () => gsTrack('audit_interacted', { product: 'audit' }, 'landing');
  const picked = (reviews?.reviews || []).filter((r) => r.text && r.text.length >= 60 && r.text.length <= 220 && r.rating5 >= 4.5).slice(0, 1);
  const faq = (cfg.faq || []).slice(0, 3);

  return (
    <div className="bm bm-root" data-screen-label="Keuring landing" ref={rootRef}>
      <div className="bm-wrap">
        <header className="bm-header"><img src="/images/bmlogo.png" alt="Brandmerck" /><span>Marketingkeuring</span></header>
        <section className="bm-hero">
          <div className="bm-hero__copy">
            <span className="bm-tag" data-anim="fade">{cfg.badge}</span>
            <h1 className="bm-h1" ref={h1Ref}>{cfg.headline}</h1>
            <p className="bm-lead" data-anim="fade">{cfg.lead}</p>
            <form className="bm-form bm-card bm-notch" id="audit-form" data-anim="fade" ref={formRef} onSubmit={submit}>
              <label className="bm-label" htmlFor="bm-url">Welke website keuren we?</label>
              <LeadinfoSuggestion company={recognized} website={url} className="bm-small" onPick={() => { leadinfoPick(recognized, setUrl, 'bm-url'); interacted(); }} />
              <input className="bm-input" id="bm-url" type="text" inputMode="url" autoComplete="url" placeholder="jouwbedrijf.nl" value={url} aria-label="Jouw website"
                onFocus={() => { kSignal('field_focus'); interacted(); }} onChange={(e) => { if (e.target.value.trim()) kSignal('field_typed'); setUrl(e.target.value); setError(''); }} />
              <button className="bm-btn" type="submit" ref={btnRef} style={{ opacity: busy ? 0.6 : 1 }}>{busy ? 'Even geduld…' : `${cfg.cta} →`}</button>
              {(error || serverError) && <p className="bm-small" style={{ color: 'var(--bm-red)', fontWeight: 700 }}>{error || serverError}</p>}

              <p className="bm-small">{cfg.micro}</p>
            </form>
          </div>
          {cfg.showReport !== false && (
            <div className="bm-hero__bon" data-anim="bon" data-signal="seen_report">
              <div className="bm-bon-stage" ref={stageRef}>
                {KEURING_CHIPS.map(([k, v], i) => <div key={k} className={`bm-chip bm-chip--${i + 1}`} aria-hidden="true"><b>{k}</b><span>{v}</span></div>)}
                <div className="bm-bon bm-bon--tilt">
                  <div className="bm-scan-beam" aria-hidden="true"></div>
                  <div className="bm-bon__meta"><span>Keuringsbon · voorbeeld</span><span><b data-countup="12">12</b> bronnen</span></div>
                  <div className="bm-bon__domain">{KEURING_VOORBEELDEN[ex].domain}</div>
                  <KeuringBonRows rows={KEURING_VOORBEELDEN[ex].rows.map(([label, tone, status]) => ({ label, tone, status }))} />
                  <div className={`bm-stamp${KEURING_VOORBEELDEN[ex].ok ? ' bm-stamp--ok' : ''}`} style={{ right: 14, bottom: -22 }} data-anim="stamp">{KEURING_VOORBEELDEN[ex].stamp}</div>
                </div>
              </div>
            </div>
          )}
        </section>
      </div>

      <section className="bm-straat" ref={straatRef}>
        <div className="bm-wrap">
          <div className="bm-straat__head"><span className="bm-kicker">De keuringsstraat</span><h2 className="bm-h2">Vijf punten. Precies waar het vaak misgaat.</h2></div>
          <div className="bm-progress"><div className="bm-progress__bar" ref={barRef} style={{ width: '20%' }}></div></div>
        </div>
        <div className="bm-straat__viewport">
          <div className="bm-wrap">
            <div className="bm-straat__track" ref={trackRef}>
              {KEURING_PUNTEN.map((p, i) => <article key={p.id} className={`bm-punt${i === active ? ' is-active' : ''}`}><span className="bm-punt__nr">{p.nr}</span><strong>{p.title}</strong><p>{p.text}</p></article>)}
            </div>
          </div>
        </div>
      </section>

      {cfg.showMascot !== false && (
        <div className="bm-wrap">
          <div className="bm-hoekie" data-anim="fade">
            <img src="/images/hoekie-keuring.png" alt="Hoekie, de hond van Brandmerck, met een vergrootglas" data-hoekie />
            <div className="bm-hoekie__bubble bm-card">Ik keur, jij beslist. Geen verkooppraatje, gewoon de bon.</div>
          </div>
        </div>
      )}

      <section className="bm-daarna bm-dark">
        <div className="bm-wrap bm-daarna__inner">
          <span className="bm-kicker">En daarna?</span>
          <h2 className="bm-h2">Een mens kijkt mee. Als jij dat wilt.</h2>
          <p className="bm-lead">Na de keuring kun je een echte second opinion krijgen van een specialist van Brandmerck. Jij kiest hoe:</p>
          <div className="bm-optlist"><div><b>1</b>Een persoonlijke video over jouw bon</div><div><b>2</b>Een videocall van 25 minuten</div><div><b>3</b>Gewoon even gebeld worden</div></div>
        </div>
      </section>

      {(cfg.showFaq !== false || cfg.showReviews !== false) && (
        <div className="bm-wrap">
          <section className="bm-faq">
            {cfg.showFaq !== false && <div data-signal="seen_faq"><h2 className="bm-h2" style={{ fontSize: 26 }}>Eerlijke vragen</h2>{faq.map((f, i) => <div key={i} className="bm-faq__item"><strong>{f.q}</strong><p>{f.a}</p></div>)}</div>}
            {cfg.showReviews !== false && picked.map((r, i) => (
              <figure key={i} className="bm-review bm-card" style={{ margin: 0 }} data-signal="seen_reviews">
                <span className="bm-review__stars" aria-label={`${nlRating(r.rating5)} van 5`}>{'★★★★★'.slice(0, Math.round(r.rating5))}</span>
                <p>"{r.text}"</p>
                <figcaption className="bm-small"><strong style={{ color: 'var(--bm-text)' }}>{r.name}</strong> · {r.source === 'google' ? 'via Google' : 'via The Feedback Company'}{reviews?.combined ? ` · ${nlRating(reviews.combined.rating5)} uit ${reviews.combined.total} reviews` : ''}</figcaption>
              </figure>
            ))}
          </section>
        </div>
      )}

      <div className={`bm-sticky${sticky ? ' is-visible' : ''}`}>
        <span>Keuring duurt 3 minuten</span>
        <a className="bm-btn bm-btn--sm" href="#bm-url" onClick={(e) => { e.preventDefault(); document.getElementById('bm-url')?.scrollIntoView({ behavior: 'smooth', block: 'center' }); setTimeout(() => document.getElementById('bm-url')?.focus(), 400); }}>{cfg.cta} →</a>
      </div>
    </div>
  );
}

// ── Wachtpagina: keurpunten, drie microvragen, e-mail ─────────────────────
const KEURING_VRAGEN = [
  { id: 'services', q: 'Wat neem je af van je bureau?', hint: 'Alleen die punten keuren we streng.', multi: true, options: [['seo', 'SEO'], ['sea', 'Google Ads'], ['meta', 'Meta']] },
  { id: 'satisfaction', q: 'Hoe tevreden ben je nu over je online marketing?', hint: null, multi: false, scale: ['Ontevreden', 'Heel tevreden'], options: [[1, '1'], [2, '2'], [3, '3'], [4, '4'], [5, '5']] },
  { id: 'monthlyBudget', q: 'Wat betaal je je bureau per maand?', hint: 'Niet verplicht. Het zet de lat voor wat je mag verwachten.', multi: false, options: [['0-500', '< € 500'], ['500-1000', '€ 500 – 1.000'], ['1000-2000', '€ 1.000 – 2.000'], ['2000-5000', '€ 2.000 – 5.000'], ['5000+', '€ 5.000+']] },
];
function KeuringScan({ auditId, onDone, onRestart }) {
  const { useState, useEffect, useRef } = React;
  const [state, setState] = useState(null); const [errorMsg, setErrorMsg] = useState('');
  const [qi, setQi] = useState(0); const [answers, setAnswers] = useState({ services: [], satisfaction: null, monthlyBudget: null });
  const [mailOpen, setMailOpen] = useState(false); const [mail, setMail] = useState({ value: '', busy: false, done: false, error: '' });
  const [report, setReport] = useState(null); const [refining, setRefining] = useState(false);
  const doneRef = useRef(false); const finalRef = useRef({ acked: false, since: 0 }); const stampedRef = useRef(new Set());
  const onDoneRef = useRef(onDone); onDoneRef.current = onDone;

  useEffect(() => {
    let stopped = false;
    async function poll() {
      const startedAt = Date.now();
      try {
        const res = await fetch(`/api/audit/${auditId}`);
        if (res.status === 404) { if (!stopped) setErrorMsg('De keuring is verlopen. Start opnieuw.'); return; }
        const d = await res.json(); if (stopped) return;
        setState(d);
        if (d.audit?.status === 'error') setErrorMsg(d.audit.error || 'Er ging iets mis.');
        const fin = finalRef.current;
        if (d.audit?.status === 'done' && d.report && fin.acked && startedAt >= fin.since && !doneRef.current) { doneRef.current = true; setReport(d.report); setTimeout(() => onDoneRef.current(d.report), 1400); }
      } catch { /* netwerk-hikje */ }
    }
    poll(); const iv = setInterval(poll, 2000); const t = setTimeout(() => setMailOpen(true), 20000);
    return () => { stopped = true; clearInterval(iv); clearTimeout(t); };
  }, [auditId]);

  function post(next, final = false) {
    return fetch(`/api/audit/${auditId}/answers`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...next, final }) }).then((r) => r.json()).catch(() => ({}));
  }
  function advance(next) {
    setMailOpen(true);
    if (qi + 1 < KEURING_VRAGEN.length) { post(next); setQi(qi + 1); return; }
    setQi(KEURING_VRAGEN.length); finalRef.current = { acked: false, since: 0 };
    post(next, true).then((r) => { finalRef.current = { acked: true, since: Date.now() }; setRefining(!!r?.refining); });
  }
  const q = KEURING_VRAGEN[qi];
  function choose(val) {
    const next = { ...answers };
    if (q.multi) next[q.id] = next[q.id].includes(val) ? next[q.id].filter((x) => x !== val) : [...next[q.id], val]; else next[q.id] = val;
    setAnswers(next);
    if (!q.multi) setTimeout(() => advance(next), 350);
  }
  async function leaveEmail(e) {
    e.preventDefault(); const v = mail.value.trim();
    if (!/^\S+@\S+\.\S+$/.test(v)) { setMail((m) => ({ ...m, error: 'Vul een geldig e-mailadres in' })); return; }
    setMail((m) => ({ ...m, busy: true, error: '' }));
    try {
      const r = await fetch(`/api/audit/${auditId}/email`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: v }) });
      const d = await r.json().catch(() => ({})); if (!r.ok) throw new Error(d.error || 'Dat lukte niet');
      gsTrack('audit_email_left', { product: 'audit', audit_id: auditId }, `${auditId}:email`);
      setMail({ value: v, busy: false, done: true, error: '' });
    } catch (err) { setMail((m) => ({ ...m, busy: false, error: err.message })); }
  }

  const expected = state?.expected || []; const progress = state?.audit?.progress?.checks || {};
  const allDone = state?.audit?.status === 'done';
  const checks = KEURING_PUNTEN.map((p) => {
    const srcs = p.sources.filter((s) => expected.includes(s));
    const st = srcs.map((s) => progress[s]?.status);
    const stateOf = allDone ? 'done' : !srcs.length ? 'wait' : st.every((x) => x && x !== 'running') ? 'done' : 'wait';
    return { ...p, state: stateOf };
  });
  // De keuring is altijd ergens mee bezig: het eerste punt dat nog niet klaar is, staat op "bezig"
  if (!allDone) { const i = checks.findIndex((c) => c.state !== 'done'); if (i >= 0) checks[i].state = 'busy'; }
  const doneCount = checks.filter((c) => c.state === 'done').length;
  const sourcesDone = Object.values(progress).filter((c) => c.status && c.status !== 'running').length;
  const stage = state?.audit?.progress?.stage || 'start';
  const stageLabel = allDone ? 'klaar' : stage === 'answers' ? 'wachten op je antwoorden' : stage === 'scoring' ? 'punten tellen' : stage === 'narrative' ? 'bon schrijven' : stage.startsWith('checks') ? `bron ${Math.min(sourcesDone + 1, expected.length || 12)} van ${expected.length || 12} meten` : 'opstarten';
  const pct = allDone ? 100 : stage === 'narrative' ? 95 : stage === 'scoring' ? 90 : Math.max(6, Math.round((sourcesDone / Math.max(1, expected.length)) * 85));
  useEffect(() => { checks.forEach((c) => { if (c.state === 'done' && !stampedRef.current.has(c.id)) { stampedRef.current.add(c.id); kStampIn(document.querySelector(`[data-check="${c.id}"] .bm-check__stamp`)); } }); });
  useEffect(() => { const g = kMotion(); const el = document.querySelector('[data-klaar]'); if (report && g && el) g.fromTo(el, { scale: 0.96 }, { scale: 1, duration: 0.5, ease: 'back.out(3)' }); }, [report]);

  if (errorMsg) return (
    <div className="bm bm-root"><main className="bm-scan"><header className="bm-header"><img src="/images/bmlogo.png" alt="Brandmerck" style={{ height: 24 }} /></header><div className="bm-card" style={{ padding: 22 }}><strong>{errorMsg}</strong><div style={{ marginTop: 12 }}><button className="bm-btn bm-btn--sm" type="button" onClick={onRestart}>Opnieuw beginnen</button></div></div></main></div>
  );
  return (
    <div className="bm bm-root" data-screen-label="Keuring bezig">
      <main className="bm-scan">
        <header className="bm-header" style={{ paddingTop: 18 }}><img src="/images/bmlogo.png" alt="Brandmerck" style={{ height: 24 }} /><span aria-live="polite">{allDone ? 'Klaar' : doneCount >= 5 ? 'Alles gekeurd' : `Punt ${Math.min(doneCount + 1, 5)} van 5`}</span></header>
        <div className="bm-scan__title">
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}><span className="bm-kicker">{allDone ? 'Keuring klaar' : doneCount >= 5 ? 'Bon wordt geschreven' : 'Keuring bezig · ±3 min'}</span><h1>{allDone ? <>De bon van <span>{state?.audit?.domain}</span> is klaar</> : doneCount >= 5 ? <>Alle punten gekeurd, de bon wordt geschreven</> : <><span>{state?.audit?.domain || 'jouwbedrijf.nl'}</span> ligt op de brug</>}</h1></div>
        </div>
        <div className="bm-progress" aria-label="Voortgang" style={{ marginBottom: 14 }}><div className="bm-progress__bar" style={{ width: `${pct}%`, transition: 'width .8s ease' }}></div></div>
        <p className="bm-small" style={{ marginTop: -8, marginBottom: 14, display: 'flex', alignItems: 'center', gap: 8 }}>{!allDone && <span className="bm-spinner" aria-hidden="true"></span>}Nu: {stageLabel}</p>
        <section className="bm-checks bm-card" aria-label="Voortgang keuring">
          {checks.map((c) => (
            <div key={c.id} className="bm-check" data-check={c.id} data-state={c.state}>
              <span className="bm-check__name">{c.title}</span>
              <span className="bm-check__status">{c.state === 'done' ? <span className="bm-check__stamp">GEKEURD</span> : c.state === 'busy' ? <><span className="bm-spinner" aria-hidden="true"></span>bezig</> : 'wacht'}</span>
            </div>
          ))}
        </section>

        {q && (
          <section className="bm-question bm-dark">
            <span className="bm-question__meta">Terwijl we keuren · vraag {qi + 1} van {KEURING_VRAGEN.length}</span>
            <strong>{q.q}</strong>
            <div className={`bm-scores${q.options.length === 3 ? ' bm-scores--3' : q.options.length === 4 ? ' bm-scores--4' : q.options.length === 5 && q.id !== 'satisfaction' ? ' bm-scores--4' : ''}`} role="group">
              {q.options.map(([val, label]) => { const on = q.multi ? answers[q.id].includes(val) : answers[q.id] === val; return <button key={String(val)} className="bm-score" type="button" aria-pressed={on} onClick={() => choose(val)}>{label}</button>; })}
            </div>
            {q.scale && <div className="bm-question__scale"><span>{q.scale[0]}</span><span>{q.scale[1]}</span></div>}
            {q.hint && <span className="bm-small" style={{ color: 'var(--bm-muted-on-dark)' }}>{q.hint}</span>}
            {q.multi && <button className="bm-btn bm-btn--sm" type="button" style={{ alignSelf: 'flex-start' }} onClick={() => advance(answers)}>{answers[q.id].length ? 'Verder →' : 'Overslaan →'}</button>}
          </section>
        )}

        {mailOpen && !state?.audit?.hasEmail && !allDone && (
          <section className="bm-mail">
            {!mail.done ? (
              <form onSubmit={leaveEmail} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                <label htmlFor="bm-mail">Geen zin om te wachten? We mailen je de bon zodra hij klaar is.</label>
                <div className="bm-mail__row"><input className="bm-input" id="bm-mail" type="email" autoComplete="email" placeholder="naam@jouwbedrijf.nl" value={mail.value} onChange={(e) => setMail((m) => ({ ...m, value: e.target.value }))} /><button className="bm-btn bm-btn--sm" type="submit" disabled={mail.busy}>Mail mij</button></div>
                {mail.error && <span className="bm-small" style={{ color: 'var(--bm-red)', fontWeight: 700 }}>{mail.error}</span>}
                <span className="bm-small" style={{ fontSize: 12 }}>Alleen voor je bon. Geen nieuwsbrief.</span>
              </form>
            ) : (
              <div className="bm-mail__done" aria-live="polite"><strong>Komt eraan.</strong><span>Je bon staat over een paar minuten in je inbox. Je mag dit scherm gerust sluiten.</span></div>
            )}
          </section>
        )}

        <div className="bm-scan__foot">
          {report ? <button className="bm-btn" type="button" data-klaar onClick={() => onDone(report)}>Bekijk je keuringsbon →</button>
            : <span className="bm-btn bm-btn--quiet" aria-disabled="true" style={{ gap: 10 }}><span className="bm-spinner" aria-hidden="true"></span>{allDone ? (qi < KEURING_VRAGEN.length ? 'Bon is klaar: nog even de vragen hierboven' : refining ? 'We verwerken je antwoorden in de bon…' : 'Bon wordt geschreven…') : doneCount >= 5 ? 'Punten tellen en bon schrijven, nog even…' : 'Of wacht even: bon bijna klaar'}</span>}
        </div>
      </main>
    </div>
  );
}

// ── Keuringsbon (rapport) ────────────────────────────────────────────────
// Schaal per keurpunt: de score van 0 tot 100 met een woord erbij, in de kleur van het stoplicht
function kScale(score, light, gated) {
  if (gated) return { tone: 'none', label: 'Niet van toepassing' };
  if (score == null) return { tone: 'none', label: 'Niet gekeurd' };
  const tone = (KEURING_LIGHT[light] || KEURING_LIGHT.grey)[0];
  const label = score >= 85 ? 'Goed' : score >= 65 ? 'Redelijk' : score >= 45 ? 'Matig' : 'Zwak';
  return { tone, label };
}
function keuringFindings(report) {
  const scores = report?.scores || {}; const n = report?.narrative || {};
  return KEURING_PUNTEN.map((p) => {
    const s = scores[p.dim] || {}; const { tone, label } = kScale(s.score, s.light, !!s.facts?.gated);
    // één zin per keurpunt: de eerste zin van de toelichting, anders de eerste reden
    const first = (t) => { const m = String(t || '').match(/^.*?[.!?](\s|$)/); return m ? m[0].trim() : String(t || '').trim(); };
    const text = first(n[p.dim]?.finding) || (s.reasons || [])[0] || (s.facts?.gated ? 'Niet van toepassing: je neemt Google Ads niet af.' : 'Hier konden we te weinig van buitenaf meten.');
    return { ...p, tone, label, score: s.score ?? null, light: s.light, reasons: s.reasons || [], finding: n[p.dim]?.finding || '', evidence: n[p.dim]?.evidence || '', meaning: n[p.dim]?.meaning || '', text };
  });
}
function KeuringBon({ report, onOpinion, onFull, onRestart }) {
  const { useEffect, useRef } = React;
  useReportTracking(report?.shareToken, 'keuring');
  const rootRef = useRef(null);
  const findings = keuringFindings(report); const total = report?.scores?.total ?? null;
  const verdict = report?.scores?.verdict || 'insufficient_data'; const conclusie = report?.narrative?.conclusie || {};
  const isBad = (r) => /niet|geen|ontbreekt|te lang|daalt|traag|rood|oranje|zonder|lang geleden|blijft achter|afgenomen dienst/i.test(r);
  const date = new Date(report?.audit?.finishedAt || report?.audit?.createdAt || Date.now()).toLocaleDateString('nl-NL', { day: 'numeric', month: 'long', year: 'numeric' });
  // looptijd van de meting; na een herschrijving met antwoorden klopt het verschil niet meer, dan ±3
  const rawMin = report?.audit?.finishedAt && report?.audit?.createdAt ? Math.round((new Date(report.audit.finishedAt) - new Date(report.audit.createdAt)) / 60000) : 0;
  const minutes = rawMin >= 1 && rawMin <= 8 ? rawMin : 3;
  const summary = report?.narrative?.conclusie?.samenvatting || report?.narrative?.summary || '';
  useEffect(() => {
    const g = kMotion(); const root = rootRef.current; if (!g || !root) return;
    const rep = root.querySelector('[data-report]');
    g.fromTo(rep, { clipPath: 'inset(0 0 100% 0)' }, { clipPath: 'inset(0 0 0% 0)', duration: 1.2, ease: 'power2.inOut' });
    g.from(root.querySelectorAll('[data-finding]'), { x: -12, autoAlpha: 0, duration: 0.4, stagger: 0.12, delay: 0.5, ease: 'power2.out' });
    kCountUp(root.querySelector('[data-countup]'), total ?? 0, 0.4); kStampIn(root.querySelector('[data-anim="stamp-late"]'), 1.5);
    g.from(root.querySelectorAll('.bm-finding__bar > span'), { scaleX: 0, transformOrigin: 'left center', duration: 0.8, stagger: 0.12, delay: 0.7, ease: 'power2.out' });
    const cleanup = kMagnet(root.querySelector('[data-magnet]'));
    return () => cleanup && cleanup();
  }, []);
  return (
    <div className="bm bm-root" data-screen-label="Keuringsbon" ref={rootRef}>
      <div className="bm-wrap" style={{ maxWidth: 560 }}><header className="bm-header"><img src="/images/bmlogo.png" alt="Brandmerck" style={{ height: 24 }} /><span>Keuring klaar</span></header></div>
      <main className="bm-rapport">
        <article className="bm-report bm-card" data-report>
          <div className="bm-bon__meta"><span>Keuringsbon</span><span>12 bronnen · {minutes} min</span></div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}><h1>{report?.audit?.domain}</h1><span className="bm-report__date">Gekeurd op {date}</span></div>
          <div className="bm-report__score"><b data-countup={total ?? 0}>{total ?? '–'}</b><span>van 100 punten<br />op vijf keurpunten</span></div>
          <div>
            {findings.map((f) => (
              <div key={f.id} className="bm-finding" data-finding>
                <div className="bm-finding__head"><strong>{f.title}</strong><strong className={`bm-status--${f.tone}`}>{f.score != null && f.tone !== 'none' ? `${f.score} · ` : ''}{f.label}</strong></div>
                <div className={`bm-finding__bar bm-finding__bar--${f.tone}`} aria-hidden="true"><span style={{ width: `${f.score ?? 0}%` }}></span></div>
                <p>{f.text}</p>
              </div>
            ))}
          </div>
          {summary && <div className="bm-plain"><strong>In gewone taal:</strong> {summary}</div>}
          <p className="bm-small" style={{ margin: 0 }}>Gekeurd van buitenaf, op publieke data en zonder toegang tot je accounts. Een lager punt kan een verklaring hebben die wij van buiten niet zien; sluitende antwoorden geeft alleen een second opinion.</p>
          <div className="bm-stamp" data-anim="stamp-late" dangerouslySetInnerHTML={{ __html: KEURING_STAMP[verdict] || KEURING_STAMP.insufficient_data }}></div>
        </article>
        <section className="bm-diep" aria-label="De keuring in detail">
          <div className="bm-diep__head"><span className="bm-kicker">De keuring in detail</span><h2 className="bm-h2" style={{ fontSize: 24 }}>Wat we per punt zagen</h2></div>
          {findings.map((f) => (
            <details key={f.id} className="bm-diep__punt bm-card" onToggle={(e) => { if (e.currentTarget.open) reportEvent(`detail:${f.id}`); }}>
              <summary><span className={`bm-diep__dot bm-diep__dot--${f.tone}`}></span><strong>{f.title}</strong><em className={`bm-status--${f.tone}`}>{f.score != null && f.tone !== 'none' ? `${f.score} · ` : ''}{f.label}</em></summary>
              <div className="bm-diep__body">
                {f.finding && <p><strong>{f.finding}</strong></p>}
                {f.reasons.length > 0 && <ul className="bm-diep__list">{f.reasons.map((r, i) => <li key={i} className={isBad(r) ? 'is-bad' : 'is-good'}>{r}</li>)}</ul>}
                {f.evidence && <p>{f.evidence}</p>}
                {f.meaning && <p className="bm-diep__meaning"><strong>Wat dit betekent:</strong> {f.meaning}</p>}
              </div>
            </details>
          ))}
          {(conclusie.sterk?.length || conclusie.aandacht?.length) ? (
            <div className="bm-diep__sum">
              {conclusie.sterk?.length > 0 && <div className="bm-card"><span className="bm-kicker" style={{ color: 'var(--bm-teal)' }}>Sterk</span><ul className="bm-diep__list">{conclusie.sterk.map((t, i) => <li key={i} className="is-good">{t}</li>)}</ul></div>}
              {conclusie.aandacht?.length > 0 && <div className="bm-card"><span className="bm-kicker">Vraagt aandacht</span><ul className="bm-diep__list">{conclusie.aandacht.map((t, i) => <li key={i} className="is-bad">{t}</li>)}</ul></div>}
            </div>
          ) : null}
          <p className="bm-small" style={{ textAlign: 'center', marginTop: 6 }}><a href="#volledig" onClick={(e) => { e.preventDefault(); reportEvent('volledig'); onFull(); }}>Bekijk de volledige keuring met alle metingen en grafieken</a></p>
        </section>
      </main>
      <section className="bm-cta bm-dark">
        <div className="bm-cta__inner">
          <div className="bm-cta__hoekie"><img src="/images/hoekie-keuring.png" alt="Hoekie" /><p>Dit is wat de machine ziet. Wat betekent het voor jóuw bedrijf?</p></div>
          <h2 className="bm-h2" style={{ fontSize: 28 }}>Laat een specialist de bon doorlopen.</h2>
          <button className="bm-btn" type="button" data-magnet onClick={() => { reportEvent('second_opinion_klik'); onOpinion(); }}>Vraag een second opinion →</button>
          <button className="bm-btn bm-btn--ghost-dark" type="button" onClick={() => { reportEvent('pdf'); window.print(); }}>Bewaar de bon als PDF</button>
          <p className="bm-small" style={{ color: 'var(--bm-muted-on-dark)' }}><a href="#opnieuw" onClick={(e) => { e.preventDefault(); onRestart(); }} style={{ color: 'inherit' }}>Nog een website keuren</a></p>
        </div>
      </section>
    </div>
  );
}

// ── Second opinion: video, videocall of bellen ───────────────────────────
function keuringSlots() {
  const out = []; const d = new Date(); d.setDate(d.getDate() + 1);
  while (out.length < 3) { if (d.getDay() >= 1 && d.getDay() <= 5) { const lbl = d.toLocaleDateString('nl-NL', { weekday: 'short', day: 'numeric', month: 'short' }); out.push(`${lbl} 10:00`, `${lbl} 13:30`, `${lbl} 16:00`); } d.setDate(d.getDate() + 1); }
  return out.slice(0, 6);
}
function KeuringOpinion({ report, onBack }) {
  const { useState, useEffect, useRef } = React;
  const rootRef = useRef(null);
  const [type, setType] = useState('video'); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [phone, setPhone] = useState(''); const [slot, setSlot] = useState(keuringSlots()[1]);
  const [busy, setBusy] = useState(false); const [done, setDone] = useState(false); const [error, setError] = useState('');
  const slots = keuringSlots(); const ctas = { video: 'Stuur mij de video →', call: 'Plan de videocall →', phone: 'Bel mij terug →' };
  useEffect(() => { const g = kMotion(); if (g && rootRef.current) g.from(rootRef.current.querySelectorAll('.bm-choice'), { y: 16, autoAlpha: 0, stagger: 0.07, duration: 0.4, ease: 'power2.out' }); }, []);
  useEffect(() => { const g = kMotion(); const el = rootRef.current?.querySelector('[data-detail]'); if (g && el) g.from(el, { y: 12, autoAlpha: 0, duration: 0.3, ease: 'power2.out' }); }, [type]);
  async function submit(e) {
    e.preventDefault(); setError('');
    if (!name.trim()) { setError('Vul je naam in'); return; }
    if (type === 'phone' ? !phone.trim() : !/^\S+@\S+\.\S+$/.test(email.trim())) { setError(type === 'phone' ? 'Vul je telefoonnummer in' : 'Vul een geldig e-mailadres in'); return; }
    setBusy(true);
    try {
      const res = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ scanId: `audit:${report.audit.id}`, name: name.trim(), email: email.trim(), phone: phone.trim(), kind: 'second_opinion', type, slot: type === 'call' ? slot : null }) });
      const d = await res.json().catch(() => ({})); if (!res.ok) throw new Error(d.error || 'Dat lukte niet');
      gsTrack('second_opinion_requested', { product: 'audit', audit_id: report.audit.id, focus: type }, report.audit.id);
      reportEvent(`second_opinion_verstuurd:${type}`);
      setDone(true);
    } catch (err) { setError(err.message); } finally { setBusy(false); }
  }
  const choice = (id, title, sub, pop) => <button key={id} className="bm-choice" type="button" role="radio" aria-checked={type === id} onClick={() => setType(id)}><span><strong>{title}</strong><small>{sub}</small></span>{pop && <em>POPULAIR</em>}</button>;
  return (
    <div className="bm bm-root" data-screen-label="Second opinion" ref={rootRef}>
      <main className="bm-opinion">
        <a className="bm-opinion__back" href="#bon" onClick={(e) => { e.preventDefault(); onBack(); }}>← Terug naar de bon</a>
        <section style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <span className="bm-kicker">Second opinion · gratis</span>
          <h1 className="bm-h2">Hoe wil je de uitleg krijgen?</h1>
          <p className="bm-lead" style={{ fontSize: 15 }}>Een specialist van Brandmerck kijkt naar jouw bon en zegt eerlijk wat hij ervan vindt.</p>
        </section>
        {done ? (
          <section className="bm-card" style={{ padding: 22 }}><strong>Gelukt, we nemen contact op.</strong><p className="bm-small" style={{ marginTop: 6 }}>{type === 'video' ? 'De video staat binnen twee werkdagen in je inbox.' : type === 'call' ? `We bevestigen ${slot} per mail.` : 'We bellen je binnen één werkdag.'}</p></section>
        ) : (
          <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 18, flex: 1 }}>
            <div className="bm-choices" role="radiogroup" aria-label="Kies een vorm">
              {choice('video', 'Persoonlijke video', 'Specialist bespreekt jouw bon', true)}
              {choice('call', 'Videocall, 25 min', 'Live meekijken en vragen stellen')}
              {choice('phone', 'Bel mij even', 'Kort, zonder afspraak')}
            </div>
            <div className="bm-detail bm-card" data-detail={type} key={type}>
              <label className="bm-label" htmlFor="bm-naam">Je naam</label>
              <input className="bm-input" id="bm-naam" type="text" autoComplete="name" placeholder="Voor- en achternaam" value={name} onChange={(e) => setName(e.target.value)} />
              {type === 'call' && <><span className="bm-label" style={{ marginTop: 6 }}>Kies een moment</span><div className="bm-slots">{slots.map((s) => <button key={s} className="bm-slot" type="button" aria-pressed={slot === s} onClick={() => setSlot(s)}>{s}</button>)}</div></>}
              {type !== 'phone' ? (
                <><label className="bm-label" htmlFor="bm-op-mail" style={{ marginTop: 6 }}>{type === 'video' ? 'Waar mogen we de video naartoe sturen?' : 'E-mail voor de uitnodiging'}</label><input className="bm-input" id="bm-op-mail" type="email" autoComplete="email" placeholder="naam@jouwbedrijf.nl" value={email} onChange={(e) => setEmail(e.target.value)} />{type === 'video' && <span className="bm-small">Binnen 2 werkdagen in je inbox.</span>}</>
              ) : (
                <><label className="bm-label" htmlFor="bm-op-tel" style={{ marginTop: 6 }}>Op welk nummer bellen we je?</label><input className="bm-input" id="bm-op-tel" type="tel" autoComplete="tel" placeholder="06 12345678" value={phone} onChange={(e) => setPhone(e.target.value)} /><span className="bm-small">We bellen binnen 1 werkdag.</span></>
              )}
            </div>
            <div className="bm-opinion__foot">
              {error && <span className="bm-small" style={{ color: 'var(--bm-red)', fontWeight: 700 }}>{error}</span>}
              <button className="bm-btn" type="submit" disabled={busy}>{busy ? 'Even geduld…' : ctas[type]}</button>
              <span className="bm-small">Gratis en vrijblijvend. Geen nieuwsbrief.</span>
            </div>
          </form>
        )}
      </main>
    </div>
  );
}

Object.assign(window, { KeuringLanding, KeuringScan, KeuringBon, KeuringOpinion });
